学会使用react高阶组件(HOC)优化你的代码~

React高阶组件HOC

😊文章略长,作好内心准备哦,建议阅读15分钟。html

定义

  • HOC实际上是一个函数,接收一个组件做为参数,返回一个包装组件做为返回值
  • 在多个不一样的组件中须要用到相同的功能
  • 高阶组件和装饰器就是一个模式,所以高阶组件能够做为装饰器来使用

做用

  • 适用范围广,它不须要es6或者其它须要编译的特性,有函数的地方,就有HOC。
  • Debug友好,它可以被React组件树显示,因此能够很清楚地知道有多少层,每层作了什么。

补充说明装饰器

在下面不少案例中用到了装饰器(@),这里提早说明下,修饰器(Decorator)是一个函数,用来修改类的行为react

更多用法可参考简书一篇文章:www.jianshu.com/p/275bf41f4…es6

基本形式

const EnhancedComponent = higherOrderComponent(WrappedComponent)bash

function hoc(WrappedComponent) {
    return class HOC extends React.Component {
        componentDidMount() {
            console.log("hoc");
        }

        render() {
            return <WrappedComponent />
        }
    }
}

// 使用高阶组件
class ComponentClass extends React.Component {
    render() {
        return <div></div>
    }
}

export default hoc(ComponentClass);

// 做为装饰器使用
@hoc
export default class ComponentClass extends React.Component {
    //...
}
复制代码

常见用法

  • 属性代理(Props Proxy): 高阶组件经过ComponentClass的props来进行相关操做
  • 继承反转(Inheritance Inversion)):高阶组件继承自ComponentClass

属性代理(Props Proxy)常见做用

  1. 操做props,能够对原组件的props进行增删改查,须要考虑到不能破坏原组件
// 添加新的props
function ppHOC(WrappedComponent) {
  return class PP extends React.Component {
    render() {
      const newProps = {
        user: currentLoggedInUser
      }
      return <WrappedComponent {...this.props} {...newProps}/>
    }
  }
}
复制代码
  1. 经过refs访问组件实例,进而调用组件相关方法
// WrappedComponent初始渲染时候会调用ref回调,传入组件实例,在proc方法中,就能够调用组件方法
function refsHOC(WrappedComponent) {
  return class RefsHOC extends React.Component {
    proc(wrappedComponentInstance) {
      wrappedComponentInstance.method()
    }

    render() {
      // Object.asign();将全部可枚举属性的值从一个或多个源对象复制到目标对象
      const props = Object.assign({}, this.props, {ref: this.proc.bind(this)})
      return <WrappedComponent {...props}/>
    }
  }
}
复制代码
  1. 提取state,能够经过传入 props 和回调函数把 state 提取出来
// 提取了 input 的 value 和 onChange 方法
function ppHOC(WrappedComponent) {
  return class PP extends React.Component {
    state = {
        name: ''
    }
    
    onNameChange(event) {
      this.setState({
        name: event.target.value
      })
    }
    
    render() {
      const newProps = {
        name: {
          value: this.state.name,
          onChange: this.onNameChange.bind(this)
        }
      }
       return <WrappedComponent {...this.props} {...newProps}/>
    }
  }
}

// 使用方式以下
@ppHOC
class Example extends React.Component {
  render() {
    // 使用ppHOC装饰器以后,组件的props被添加了name属性,input会成为受控组件
    return <input name="name" {...this.props.name}/>
  }
}
复制代码
  1. 用其余元素包裹WrappedComponent,实现布局,样式等目的
function ppHOC(WrappedComponent) {
  return class PP extends React.Component {
    render() {
      return (
        <div style={{display: 'block'}}>
          <WrappedComponent {...this.props}/>
        </div>
      )
    }
  }
}
复制代码

继承反转(Inheritance Inversion)常见做用

  1. 渲染劫持,HOC 控制着 WrappedComponent 的渲染输出,能够劫持被继承class的render内容,进行修改,过滤后,返回新的显示内容
// 过滤掉原组件中的ul元素
function hoc(ComponentClass) {
    return class HOC extends ComponentClass {
        render() {
            const elementTree = super.render();
            elementTree.props.children = elementTree.props.children.filter((z) => z.type !== "ul")
            
            return React.cloneElement(elementTree);
        }
    }
}

@hoc
export default class ComponentClass extends React.Component {
    render() {
        return (
            <div>
                <p style={{color: 'brown'}}>啦啦啦</p>
                <ul>
                    <li>1</li>
                    <li>2</li>
                </ul>
            </div>
        )
    }
}
复制代码
  1. 操做state,HOC能够操做WrappedComponent实例的state。但这样会破坏WrappedComponent的state,因此要限制HOC读取或添加state,添加的state应该放在单独的命名空间里
export function IIHOC(WrappedComponent) {
  return class II extends WrappedComponent {
    render() {
      return (
        <div>
          <p>Props</p> <pre>{JSON.stringify(this.props, null, 2)}</pre>
          
          <p>State</p><pre>{JSON.stringify(this.state, null, 2)}</pre>
          
          {
            super.render()
          }
        </div>
      )
    }
  }
}
复制代码
  1. 条件渲染
// 假设this.props.loggedIn为真,HOC会彻底渲染WrappedComponent 的渲染结果
function iiHOC(WrappedComponent) {
  return class ii extends WrappedComponent {
    render() {
      if (this.props.loggedIn) {
        return super.render()
      } else {
        return null
      }
    }
  }
}
复制代码
  1. 解决WrappedComponent名字丢失问题
// 用HOC包裹的组件会丢失原先的名字,影响开发和调试,能够在WrappedComponent的名字加前缀来做为HOC的名字
const componentName = WrappedComponent.displayName || WrappedComponent.name || 'Component';

static displayName = `withModal(${componentName})`;
复制代码

实际应用

  1. 记录localStorage返回值
//经过多重高阶组件肯定key并设定组件
const withStorage = (key) => WrappedComponent => {
  return class extends Component {
    componentWillMount() {
        let data = localStorage.getItem(key);
        this.setState({data});
    }
    render() {
      return <WrappedComponent data={this.state.data} {...this.props} />
    }
  }
}

@withStorage('data')
class MyComponent2 extends Component {  
    render() {
        return <div>{this.props.data}</div>
    }
}

@withStorage('name')
class MyComponent3 extends Component {  
    render() {
        return <div>{this.props.data}</div>
    }
}
复制代码
  1. 项目中,每次从新打开modal框,每次销毁modal中数据,防止数据污染
const modalHoc = (options) => WrappedComponent => {
    const componentName = WrappedComponent.displayName || WrappedComponent.name || 'Component';

    return class ModalComponent extends Component {
        static displayName = `withModal(${componentName})`;

        render() {
            const {visible, onCancel} = this.props;

            let title;
            if (typeof options === 'string') title = options;

            if (typeof options === 'function') title = options;

            if (typeof options === 'object') title = options.title;

            if (typeof title === 'function') title = title(this.props);

            return (
                <Modal
                    destroyOnClose
                    width="60%"
                    bodyStyle={{padding: 0}}
                    footer={null}

                    {...options}
                    title={title}

                    onCancel={onCancel}
                    visible={visible}
                >
                    <WrappedComponent {...this.props}/>
                </Modal>
            );
        }
    }
};

@modalHoc('能够传入不一样类型标题')
复制代码

😊 参考连接:react.html.cn/docs/higher…app

😊 刚刚加入掘金社区,欢迎提出宝贵建议,一块儿进步学习!函数

相关文章
相关标签/搜索