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);