从零开始进修 React 高阶组件

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 (
            <WrappedComponent />
          )
        }
      }
    }
  • 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 (
          <div>
            <h2>上岸</h2>
            <form onSubmit={this.login}>
              用户名: <input type="text" name="username" value={username} onChange={this.onUsernameChange}/> <br/>
              暗码: <input type="password" name="password" value={password} onChange={this.onPasswordChange}/> <br/>
              <input type="submit" value="上岸"/>
            </form>
          </div>
        )
      }
    }
  • 组件 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 (
          <div>
            <h2>注册</h2>
            <form onSubmit={this.register}>
              用户名: <input type="text" name="username" value={username} onChange={this.onUsernameChange}/> <br/>
              暗码: <input type="password" name="password" value={password} onChange={this.onPasswordChange}/> <br/>
              确认暗码: <input type="password" name="rePassword" value={rePassword} onChange={this.onRePasswordChange}/> <br/>
              <input type="submit" value="注册"/>
            </form>
          </div>
        )
      }
    }
  • 页面结果
    《从零开始进修 React 高阶组件》
  • 我们发明内里反复逻辑实在太多了,尤其是 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 (
          <div>
            <h2>注册</h2>
            <form onSubmit={this.handleSubmit}>
              用户名: <input type="text" name="username" value={username} onChange={this.composeChange('username')}/> <br/>
              暗码: <input type="password" name="password" value={password} onChange={this.composeChange('password')}/> <br/>
              确认暗码: <input type="password" name="rePassword" value={rePassword} onChange={this.composeChange('rePassword')}/> <br/>
              <input type="submit" value="注册"/>
            </form>
          </div>
        )
      }
    }
  • 如今两个页面都有 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 (
            <div>
              {/*提取大众头部*/}
              <h2>xxx</h2>
              <WrappedComponent {...this.state} {...mapMethodToProps}/>
            </div>
          )
        }
      }
    }
    
    // 组件 Register
    class Register extends Component {
      render () {
        const { handleSubmit, composeChange, username, password, rePassword } = this.props;
        return (
          <form onSubmit={handleSubmit}>
            用户名: <input type="text" name="username" value={username} onChange={composeChange('username')}/> <br/>
            暗码: <input type="password" name="password" value={password} onChange={composeChange('password')}/> <br/>
            确认暗码: <input type="password" name="rePassword" value={rePassword} onChange={composeChange('rePassword')}/> <br/>
            <input type="submit" value="注册"/>
          </form>
        )
      }
    }
    // 向外暴露的是高阶组件的返回值~包装了 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 (
            <div>
              {/*猎取到参数值就能够一般显现了~*/}
              <h2>{title}</h2>
              <WrappedComponent {...this.state} {...mapMethodToProps}/>
            </div>
          )
        }
      }
    }
  • 在 Login / Register 组件中运用

    • export default withHoc(‘上岸’)(Login);
    • export default withHoc(‘注册’)(Register);

06、高阶组件实训3

  • 目标: 猎取父组件通报的 props
  • 修正 App 组件

    class App extends Component {
      render() {
        return (
          <div>
            {/*父组件向子组件通报属性*/}
            <Login name="jack" age={18}/>
            <Register />
          </div>
        );
      }
    }
  • 修正高阶组件

    export default (title) => (WrappedComponent) => {
      return class Form extends Component {
        ...反复代码省略...
        
        render () {
          const mapMethodToProps = {
            composeChange: this.composeChange,
            handleSubmit: this.handleSubmit,
          }
          return (
            <div>
              {/*猎取到参数值就能够一般显现了~*/}
              <h2>{title}</h2>
              {/* 将当前组件接收到的props传给包装组件~*/}
              <WrappedComponent {...this.props} {...this.state} {...mapMethodToProps}/>
            </div>
          )
        }
      }
    }
  • Login 组件中运用

     class Login extends Component {
       render () {
         const { handleSubmit, composeChange, username, password, name, age } = this.props;
         return (
           <div>
             <p>你的名字: {name}</p>
             <p>你的岁数: {age}</p>
             <form onSubmit={handleSubmit}>
               用户名: <input type="text" name="username" value={username} onChange={composeChange('username')}/> <br/>
               暗码: <input type="password" name="password" value={password} onChange={composeChange('password')}/> <br/>
               <input type="submit" value="上岸"/>
             </form>
           </div>
         )
       }
     }

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';
    }
  • 修正之前称号

《从零开始进修 React 高阶组件》

  • 修正以后称号

《从零开始进修 React 高阶组件》

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 ~~

    原文作者:xpromise
    原文地址: https://segmentfault.com/a/1190000018633603
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞