React专题—高阶组件

176 阅读3分钟

1、属性代理

【1】操作props

除了接收props,还可以在hoc中定义自定义事件,都可以通过props再传下去。

示例:

import React, { Component } from 'react';
const propsProxyHoc = WrappedComponent => class extends Component {
  handleClick() {
    console.log('click');
  }
  render() {
    const props = {
      ...this.props,
      handleClick: this.handleClick
    };
    return <WrappedComponent { ...props } />;
  }
};
export default propsProxyHoc;

【2】refs获取组件实例

当我们想获取到WrappedComponent实例时,可以通过引用ref,在组件挂载时执行ref的回调函数,在hoc中取到组件的实例。

示例:

import React, { Component } from 'react';
const refHoc = WrappedComponent => class extends Component {
  componentDidMount() {
    console.log(this.instanceComponent, 'instanceComponent');
  }
  render() {
    return (
      <WrappedComponent
            {...this.props}
        ref={instanceComponent => this.instanceComponent = instanceComponent}
      />
    );
    }
};
export default refHoc;

【3】抽离state

通过 { props, 回调函数 } 传递给wrappedComponent组件,通过回调函数获取state。这里用的比较多的就是react处理表单的时候。

示例:

// HOC
import React, { Component } from 'react';
const formCreate = WrappedComponent => class extends Component {
  constructor() {
    super();
    this.state = {
      fields: {},
    }
  }
  onChange = key => e => {
    const { fields } = this.state;
    fields[key] = e.target.value;
    this.setState({
      fields,
    })
  }
  handleSubmit = () => {
    console.log(this.state.fields);
  }
  getField = fieldName => {
    return {
      onChange: this.onChange(fieldName),
    }
  }
  render() {
    const props = {
      ...this.props,
      handleSubmit: this.handleSubmit,
      getField: this.getField,
    }
    return <WrappedComponent {...props} />;
  }
};
export default formCreate;

// 普通组件Login
import React, { Component } from 'react';
import formCreate from './form-create';
@formCreate
export default class Login extends Component {
  render() {
    return (
      <div>
        <div>
          <label id="username">账户</label>
          <input name="username" {...this.props.getField('username')}/>
        </div>
        <div>
          <label id="password">密码</label>
          <input name="password" {...this.props.getField('password')}/>
        </div>
        <div onClick={this.props.handleSubmit}>提交</div>
        <div>other content</div>
      </div>
     )
    }
}

2、反向继承-渲染劫持

HOC定义的组件继承了WrappedComponent的render,我们可以以此进行劫持,来控制它的render函数。

示例:

// HOC
import React from 'react';
const renderHoc = config => WrappedComponent => class extends WrappedComponent {
  render() {
    const { style = {} } = config;
    const elementsTree = super.render();
    if (config.type === 'add-style') {
      return (
        <div style={{...style}}>{elementsTree}</div>;
            );
        }
        return elementsTree;
    }
};
export default renderHoc;

//usual
@renderHoc({type: 'add-style', style: { color: 'red'}})
class Usual extends Component {
  ...
}

3、应用场景

a. 多个页面UI和功能几乎一样,如果写了多个耦合性很高的页面级组件,维护它的时候,就会出现添加一个功能,改完第一个地方的时候,还要去改其他类似的地方。

b. 写完一个组件A,并且上线之后产品加了一个新需求,而且新需求中组件B跟A几乎一样,但稍微有区别。处理方式就是将不同的地方放到HOC里定义的state中,再通过劫持渲染,把不同的地方,添加的地方进行处理。

4、注意事项

a. 高阶组件不会修改子组件,也不拷贝子组件的行为。高阶组件只是通过组合的方式将子组件包装在容器组件中,是一个无副作用的纯函数。

b. 要给HOC添加class名,便于debugger。

c. WrappedComponent的静态方法都不会复制,如果要用需要我们单独复制。

d. HOC里指定的ref,并不会传递到子组件,如果你要使用最好写回调函数通过props传下去。

e. 不要在render方法内部使用高阶组件。高阶组件是一个纯函数,每次返回的结果都不是一个引用,React以为发生了变化,去更替这个组件会导致之前组件的状态丢失。

f. 使用compose组合HOC。

示例:

const addFuncHOC = ...
const addStyleHOC = ...//省略
const compose = (...funcs) => component => {
  if (funcs.lenght === 0) {
    return component;
  }
  const last = funcs[funcs.length - 1];
  return funcs.reduceRight((res, cur) => cur(res), last(component));
};
const WrappedComponent = compose(addFuncHOC, addStyleHOC)(Usual);