美文网首页
不积跬步--使用react 高阶组件的正确姿势

不积跬步--使用react 高阶组件的正确姿势

作者: 雨飞飞雨 | 来源:发表于2019-04-27 22:15 被阅读0次

    知识的学习一定要诚信于自己。

    高阶函数的基本概念

    1.函数可以作为参数被传递,如:

    setTimeout(()=>{
        console.log(1)
    },1000)
    

    2.函数可以作为返回值被输出,如:

    function foo(x){
        return function(){
            return x; 
        }
    }
    

    高阶组件的基本概念(High Order Component,HOC)

    1.高阶组件就是接受一个组件作为参数并返回一个新的组件
    2.高阶组件是一个函数,并不是一个组件

    高阶组件就是一个简单的函数,它会接收一个React组件作为参数,然后返回另一个React组件。
    通常,HOC会使用一个能够维护State或包含若干功能的类来包装输入的组件。高阶组件是组件之间功能复用的最佳方式。

    高阶组件.png

    高阶组件的实现方式

    属性代理

    我们可以看到下面高阶组件最简单的实现方式。我们把一个组件作为参数传入到我们的函数里,在我们的函数里我们定义了一个类继承了React.Component,然后通过render的方法中通过return把传入的组件返回。这样我们就可以代理所有传入的props,并且决定如何渲染他们。实际上高阶组件就是原组件的父组件。这就是属性代理的实现方式。

    function ComponentHOC(WrappedComponent) {
        return class extends React.Component {
           render() {
               return <WrappedComponent {...this.props}/>
           }
        }
    }
    

    对比原生组件增强的项:

    • 可操作所有穿传入的props
    • 可操作组件的生命周期
    • 可操作组件的static方法
    • 获取refs

    反向继承

    反向继承是返回一个组件,这个组件继承传入的组件,然后在render中调用继承组件的render方法。由于继承于原组件,能通过this访问到原组件的生命周期,props,state,render等,相比属性代理它能操作更多的属性。

    function ConsoleHOC(WrappedComponent) {
        return class extends WrappedComponent {
            render(){
                return super.render();
            }
        }
    }
    

    对比原生组件增强的项:

    • 可操作所有传入的props
    • 可操作组件的生命周期
    • 可操作组件的static方法
    • 获取refs
    • 可操作state
    • 可以渲染劫持

    HOC可以实现什么功能

    组合渲染--可使用任何其他的组件和原组件进行组合渲染,达到样式,布局复用等效果。

    我们用两种实现方式分别实现以下

    //属性代理的实现方式
    function styleHOC(WrappedComponent){
        return class extends Component{
            render(){
                return (
                    <div>
                        <div className="title">{this.props.title}</div>
                        <WrappedComponent {...this.props}/>
                    </div>
                )
            }
        }
    }
    //反向继承的实现方式
    function styleHOC(WrappedComponent){
        return class extends WrappedComponent{
            render(){
                return (
                    <div>
                        <div className="title">{this.props.title}</div>
                        {super.render()}
                     </div>
                )
            }
        }
    }
    

    条件渲染--根据特定的属性决定原组件是否渲染

    //通过属性代理的方式实现
    function visibleHOC(WrappedComponent){
        return class extends Component {
            render(){
                if(this.props.visible === false) return null
                return <WrappedComponent {...this.props}/>
            }
        }
    }
    //通过反向继承的方式实现
    function visibleHOC(WrappedComponent){
        return class extends WrappedComponent{
            render(){
                if(this.props.visible === false){
                    return null
                }else{
                    return super.render()
                }
            }
        }
    }
    

    操作Props--可以对传入的props进程增加,修改,删除或者根据特定的Props进行特殊的操作:

    //通过属性代理的方式实现
    function proxyHOC(WrappedComponent){
        return class extends Component{
            render(){
                const newProps = {...this.props,user:'张三'};
                return <WrappedComponent {...newProps}/>
            }
        }
    }
    //通过反向继承实现
    function proxyHOC(WrappedComponent){
        return class extends WrappedComponent{
            render(){
                 const newProps = {...this.props,user:'张三'};
                 //doSomething...
                 return super.render();
            }
        }
    }
    

    获取refs

    我们通过获取原组件的ref来操作原组件的一些方法。关于获取ref可以参考官网的Refs & DOM这一章节。从官网中我们知道,现在它推荐使用两种方式来获取refs

    第一种是使用:React.createRef()

    class MyComponent extends React.Component {
      constructor(props) {
        super(props);
        this.myRef = React.createRef();
      }
      render() {
        return <div ref={this.myRef} />;
      }
    }
    

    第二种是使用:回调

    class MyComponent extends React.Component {
      constructor(props) {
        super(props);
        this.myRef = null;
        this.setRef=element=>{
            this.myRef = element;
        }
      }
      render() {
        return <div ref={this.setRef} />;
      }
    }
    

    我们的高阶函数同样可以使用上面的方式。

    //通过属性代理实现
    function refHOC(WrappedComponent) {
      return class extends Component {
        this.wapperRef = null;
        componentDidMount() {
          this.wapperRef.log()
        }
        render() {
          return <WrappedComponent {...this.props} ref={ref => { this.wapperRef = ref }} />;
        }
      }
    }
    

    状态管理

    将原组件的状态提取到HOC中进行管理,如下面的代码。我们将Inputvalue提取到HOC中进行管理,使它变成受控组件,同时不影响它使用onChange方法进行一些其他操作。

    //通过属性代理的方式实现
    function proxyHoc(WrappedComponent) {
      return class extends Component {
        constructor(props) {
          super(props);
          this.state = { value: '' };
        }
    
        onChange = (event) => {
          const { onChange } = this.props;
          this.setState({
            value: event.target.value,
          }, () => {
            if(typeof onChange ==='function'){
              onChange(event);
            }
          })
        }
    
        render() {
          const newProps = {
            value: this.state.value,
            onChange: this.onChange,
          }
          return <WrappedComponent {...this.props} {...newProps} />;
        }
      }
    }
    
    class HOC extends Component {
      render() {
        return <input {...this.props}></input>
      }
    }
    
    export default proxyHoc(HOC);
    

    获取state

    上面的方式实现上是高阶组件也就是父组件把它自己的实现绑定到子组件的input组件上面了。并没有直接去操作state.而通过反向继承,我们可以直接操作原组件的state.但是并不推荐直接操作或添加state.容易出事儿。

    //通过反向继承实现
    function debugHOC(WrappedComponent){
        return class extends WrappedComponent{
            render(){
                console.log('props',this.props);
                console.log('props',this.state);
                return super.render();
            }
        }
    }
    

    上面是一个简单的debug的高阶组件,我们可以写很多类似这样的调试组件,在使用的时候直接@debug.多爽。

    如何使用HOC

    HOC实际上就是一个函数,所以我们可以把它当成函数那样使用:

    function debugHOC(WrappedComponent){
        return class extends WrappedComponent{
            render(){
                console.log('props',this.props);
                console.log('props',this.state);
                return super.render();
            }
        }
    }
    
    class MyComponent extends Component{
        render(){
            return (<span>原组件</span>)
        }
    }
    
    export default debugHOC(MyComponent);
    

    Decorators 装饰器模式

    我们可以借助ES7为我们提供的Decorators来让我们的写法变的更加优雅:

    @logger
    @visible
    @style
    class Input extends Component {
      // ...
    }
    

    DecoratorsES7的一个提案,还没有被标准化,但目前Babel转码器已经支持,我们需要提前配置babel-plugin-transform-decorators-legacy

    "plugins": ["transform-decorators-legacy"]
    

    还可以结合compose函数使用:

    const hoc = compose(logger,visible,style);
    @hoc
    class Input extends Component {
        //...
    }
    

    我们暂时更新到这里...

    参考文档:
    官网高阶组件
    【React深入】从Mixin到HOC再到Hook

    相关文章

      网友评论

          本文标题:不积跬步--使用react 高阶组件的正确姿势

          本文链接:https://www.haomeiwen.com/subject/dgaqnqtx.html