资讯专栏INFORMATION COLUMN

从零开始学习 React 高阶组件

_Dreams / 1827人阅读

01、介绍

React 高阶组件也叫做 React HOC(High Order Component), 它是react中的高级技术, 用来重用组件逻辑。

但高阶组件本身并不是React API。它只是一种模式,这种模式是由react自身的组合性质必然产生的。

那么在学习高阶组件之前有一个概念我们必须清楚,就是高阶函数。

02、高阶函数

概念:高阶函数是一个函数,它接收函数作为参数或将函数作为输出返回

举个栗子:

接收函数作为参数

function a(x) {
  x();
}
function b() {
  alert("hello");
}

a(b);

将函数作为输出返回

function a() {
  function b() {
    alert("hello");
  }
  return b;
}

a()();

以上函数a就是一个高阶函数, 用法非常简单, 那么实际开发中又有哪些是高阶函数呢?

Array 的 map 、reduce 、filter 等方法

Object 的 keys 、values 等方法

03、高阶组件

概念:高阶组件就是一个函数,且该函数接受一个组件作为参数,并返回一个新的组件

举个栗子:

// WrappedComponent 就是传入的包装组件
function withHoc(WrappedComponent) {
  return class extends Component {
    render () {
      return (
        
      )
    }
  }
}

withHoc 函数就是一个高阶组件。那么高阶组件到底有什么神奇的魔力,值得我们为之着迷?

开发组件时,我们会遇到相同的功能,使用高阶组件则能减少重复代码

04、高阶组件实训1

目的: 定义高阶组件

组件 Login -- 登陆页面

// 受控组件
class Login extends Component {
  state = {
    username: "",
    password: ""
  }
  
  onUsernameChange = (e) => {
    this.setState({username: e.target.value});
  }
  
  onPasswordChange = (e) => {
    this.setState({password: e.target.value});
  }
  
  login = (e) => {
    // 禁止默认事件
    e.preventDefault();
    // 收集表单数据
    const { username, password } = this.state;
    alert(`用户名: ${username}, 密码: ${password}`);
  }
  
  render () {
    const { username, password } = this.state;
    return (
      

登陆

用户名:
密码:
) } }

组件 Register -- 注册页面

// 受控组件
class Register extends Component {
  state = {
    username: "",
    password: "",
    rePassword: ""
  }
  
  onUsernameChange = (e) => {
    this.setState({username: e.target.value});
  }
  
  onPasswordChange = (e) => {
    this.setState({password: e.target.value});
  }
  
  onRePasswordChange = (e) => {
    this.setState({rePassword: e.target.value});
  }
  
  register = (e) => {
    // 禁止默认事件
    e.preventDefault();
    // 收集表单数据
    const { username, password, rePassword } = this.state;
    alert(`用户名: ${username}, 密码: ${password}, 确认密码: ${rePassword}`);
  }
  
  render () {
    const { username, password, rePassword } = this.state;
    return (
      

注册

用户名:
密码:
确认密码:
) } }

页面效果

我们发现里面重复逻辑实在太多了,尤其是 onXxxChange 函数出现太多,我们先优化一下。

// 我们以 Register 组件为例来看
class Register extends Component {
  state = {
    username: "",
    password: "",
    rePassword: ""
  }
  // 最终修改状态数据的函数
  onChange = (stateName, stateValue) => {
    this.setState({[stateName]: stateValue});
  }
  // 高阶函数 --> 这样后面就能一直复用当前函数,而不用重新创建了~
  composeChange = (name) => {
    return (e) => this.onChange(name, e.target.value);
  }
  // 统一所有提交表单函数名
  handleSubmit = (e) => {
    e.preventDefault();
    const { username, password, rePassword } = this.state;
    alert(`用户名: ${username}, 密码: ${password}, 确认密码: ${rePassword}`);
  }
  
  render () {
    const { username, password, rePassword } = this.state;
    return (
      

注册

用户名:
密码:
确认密码:
) } }

现在两个页面都有 onChange 、 composeChange 、handleSubmit 函数和相关的状态,我们接下来提取,封装成高阶组件

// 高阶组件 withHoc
export default function withHoc(WrappedComponent) {
  return class extends Component {
    state = {
      username: "",
      password: "",
      rePassword: ""
    }
  
    onChange = (stateName, stateValue) => {
      this.setState({[stateName]: stateValue});
    }
  
    composeChange = (name) => {
      return (e) => this.onChange(name, e.target.value);
    }
    
    handleSubmit = (e) => {
      e.preventDefault();
      const { username, password, rePassword } = this.state;
      if (rePassword) {
        alert(`用户名: ${username}, 密码: ${password}, 确认密码: ${rePassword}`);
      } else {
        alert(`用户名: ${username}, 密码: ${password}`);
      }
    }
    
    render () {
      // 抽取方法
      const mapMethodToProps = {
        composeChange: this.composeChange,
        handleSubmit: this.handleSubmit,
      }
      // 将状态数据和操作的方法以 props 的方式传入的包装组件中
      return (
        
{/*提取公共头部*/}

xxx

) } } } // 组件 Register class Register extends Component { render () { const { handleSubmit, composeChange, username, password, rePassword } = this.props; return (
用户名:
密码:
确认密码:
) } } // 向外暴露的是高阶组件的返回值~包装了 Register 组件返回了一个新组件 export default withHoc(Register);

现在我们提取了公共方法、状态等数据, 封装了一个基本的高阶组件。 但是还有很多需要问题需要解决,现在开始行动~

05、高阶组件实训2

目的: 向高阶组件中传参

修改高阶组件

// 再次包裹了一层高阶函数, 这个高阶函数执行后返回值才是高阶组件
// 通过这种方式, 高阶组件内部就能获取参数了~
export default (title) => (WrappedComponent) => {
  return class Form extends Component {
    ...重复代码省略...
    
    render () {
      const mapMethodToProps = {
        composeChange: this.composeChange,
        handleSubmit: this.handleSubmit,
      }
      return (
        
{/*获取到参数值就能正常显示了~*/}

{title}

) } } }

在 Login / Register 组件中使用

export default withHoc("登陆")(Login);

export default withHoc("注册")(Register);

06、高阶组件实训3

目的: 获取父组件传递的 props

修改 App 组件

class App extends Component {
  render() {
    return (
      
{/*父组件向子组件传递属性*/}
); } }

修改高阶组件

export default (title) => (WrappedComponent) => {
  return class Form extends Component {
    ...重复代码省略...
    
    render () {
      const mapMethodToProps = {
        composeChange: this.composeChange,
        handleSubmit: this.handleSubmit,
      }
      return (
        
{/*获取到参数值就能正常显示了~*/}

{title}

{/* 将当前组件接受到的props传给包装组件~*/}
) } } }

Login 组件中使用

 class Login extends Component {
   render () {
     const { handleSubmit, composeChange, username, password, name, age } = this.props;
     return (
       

你的名字: {name}

你的年龄: {age}

用户名:
密码:
) } }

07、高阶组件实训4

目的: 修改在 React-devtool 中高阶组件名称,方便调试

修改高阶组件

export default (title) => (WrappedComponent) => {
  return class Form extends Component {
    // 定义静态方法,修改组件在调试工具中显示的名称
    static displayName = `Form(${getDisplayName(WrappedComponent)})`
    
    ...省略重复代码...
  }
}
// 获取包装组件的displayName的方法
function getDisplayName(WrappedComponent) {
  return WrappedComponent.displayName || WrappedComponent.name || "Component";
}

修改之前名称

修改之后名称

08、使用装饰器

目的: 简化使用高阶组件

下载包

npm i react-app-rewired customize-cra @babel/plugin-proposal-decorators -D

在项目根目录配置 config-overrides.js

const { override, addDecoratorsLegacy } = require("customize-cra");
// 修改 create-react-app 的 webpack 的配置
module.exports = override(
  addDecoratorsLegacy()
)

修改 package.json 的 scripts

// 将 react-scripts 修改为 react-app-rewired
"scripts": {
  "start": "react-app-rewired start",
  "build": "react-app-rewired build",
  "test": "react-app-rewired test"
},

以上就是使用 decorator 的配置,修改完后就能使用了~

修改 Login 组件

@withHoc("登陆")
class Login extends Component {
  ...省略重复代码...
}
export default Login;

修改 Register 组件

@withHoc("注册")
class Register extends Component {
  ...省略重复代码...
}
export default Register;

react-app-rewired customize-cra 是 create-react-app 2.0以上专门用来修改 webpack 的配置

decorator 还能做很多事,感兴趣朋友可以看看 阮一峰ES6教程 了解更多

重复代码永远是我们需要考虑处理的代码,所以我们有模块化、组件化、工具类函数等等,
在 React 中再次引入了一个高阶组件的概念,都是为了去除掉万恶的重复代码,让我们代码变得更加精简。
本篇文章所有源码都放在了 git仓库,如果它对你有帮助的话,欢迎点 star ~~

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/102900.html

相关文章

  • 从零开始编写React-Express单页博客应用(学习总结)

    摘要:单页博客应用编写总结很久之前就想写一个博客应用在一开始想要直接用和模板直接写但是暑假一开始的时候不小心入了的坑所以就一不做二不休直接用写那既然用了不写个单页应用也过意不去了不前前后后写了将近两个星期现在看来这其实是一个很容易的应用但是鉴于 React-Express单页博客应用编写总结 很久之前就想写一个博客应用.在一开始想要直接用express和ejs模板直接写, 但是暑假一开始的时...

    Jioby 评论0 收藏0
  • 写一本关于 React.js 的小书

    摘要:因为工作中一直在使用,也一直以来想总结一下自己关于的一些知识经验。于是把一些想法慢慢整理书写下来,做成一本开源免费专业简单的入门级别的小书,提供给社区。本书的后续可能会做成视频版本,敬请期待。本作品采用署名禁止演绎国际许可协议进行许可 React.js 小书 本文作者:胡子大哈本文原文:React.js 小书 转载请注明出处,保留原文链接以及作者信息 在线阅读:http://huzi...

    Scorpion 评论0 收藏0
  • 前端每周清单半年盘点之 ReactReactNative 篇

    摘要:前端每周清单半年盘点之与篇前端每周清单专注前端领域内容,以对外文资料的搜集为主,帮助开发者了解一周前端热点分为新闻热点开发教程工程实践深度阅读开源项目巅峰人生等栏目。与求同存异近日,宣布将的构建工具由迁移到,引发了很多开发者的讨论。 前端每周清单半年盘点之 React 与 ReactNative 篇 前端每周清单专注前端领域内容,以对外文资料的搜集为主,帮助开发者了解一周前端热点;分为...

    Barry_Ng 评论0 收藏0
  • Vue.js最佳实践(五招让你成为Vue.js大师)

    摘要:但如果你想更加高效地使用来开发,成为大师,那下面我要传授的这五招你一定得认真学习一下了。虽然损失了一丢丢性能,但避免了无限的。所以我们需要设置,这些默认行为将会被去掉以上两点的优化才能成功。陆续可能还会更新一些别的招数,敬请期待。 本文面向对象是有一定Vue.js编程经验的开发者。如果有人需要Vue.js入门系列的文章可以在评论区告诉我,有空就给你们写。 对大部分人来说,掌握Vue.j...

    CocoaChina 评论0 收藏0

发表评论

0条评论

_Dreams

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<