高阶组件就是一个函数,接收一个组件,返回一个新的组件,可以用来扩展传入的组件的功能

注:在高阶组件之前通过 mixins 来实现相同的功能,官方不推荐使用 mixins

声明高阶组件

function higherOrderComponent(WrappedComponent) {
return class extends React.Component {
render() {
return <WrappedComponent />;
}
};
}

高阶组件的调用

const EnhancedComponent = higherOrderComponent(WrappedComponent);

举个例子,在组件里面注入属性传递日志

function logProps(WrapperComponent) {
return class extends React.Component {
componentWillReceiveProps(nextProps) {
console.log(`当前属性`, this.props);
console.log(`新的属性`, nextProps);
}
render() {
return <WrapperComponent {...this.props} />;
}
};
}
class MyComponent extends React.Component {
constructor(props) {
super(props);
}
render() {
return <h1>{this.props.name}</h1>;
}
}
const EnhanceComponent = logProps(MyComponent);
class App extends Component {
state = {
name: 'name',
pwd: 'password',
};
handleClick = () => {
this.setState({
name: Math.random(),
});
};
render() {
return (
<div>
<EnhanceComponent name={this.state.name} /> 
<button onClick={this.handleClick}>click</button>
</div>
);
}
}

注意事项

  • 高阶组件不应该改变原来的组件,而应该组合成一个新的组件,类似纯函数的思想
  • 不要在 render 方法里面使用高阶组件,应该再组件定义外部调用高阶组件
  • 如果组件有静态方法必须一同拷贝
// 组件定义静态方法
WrappedComponent.staticMethod = function () {
/*...*/
};
// 调用高阶组件函数
const EnhancedComponent = enhance(WrappedComponent);
// 返回的组件没有静态方法
typeof EnhancedComponent.staticMethod === 'undefined'; // true

解决方法就是在高阶组件里面拷贝对应的具体静态方法

function enhance(WrappedComponent) {
class Enhance extends React.Component {
/*...*/
} // 拷贝指定的静态方法
Enhance.staticMethod = WrappedComponent.staticMethod;
return Enhance;
}

已经有封装好的库来实现静态方法的拷贝[hoist-non-react-statics](https://github.com/mridgway/hoist-non-react-statics)

还有另一种方法,是把组件的静态方法 export 出来,例如

MyComponent.someFunction = someFunction;
export default MyComponent;
// 导出静态方法
export { someFunction };
// 使用时候导入相应的静态方法
import MyComponent, { someFunction } from './MyComponent.js';
  • props 可以通过高阶组件传递,ref 却不传递,如果给元素添加 ref 属性,ref 引用的是最高层组件,而不是包装的组件

render props

render props 通过过传递函数给子元素调用,该函数会返回需要渲染的元素或者组件

官方给的例子

class Mouse extends React.Component {
constructor(props) {
super(props);
this.state = { x: 0, y: 0 };
this.handleMouseMove = this.handleMouseMove.bind(this);
}
handleMouseMove(event) {
this.setState({
x: event.pageX,
y: event.pageY,
});
}
render() {
return (
<div
style={{
position: 'relative',
width: '300px',
height: '300px',
border: '1px solid #333',
}}
onMouseMove={this.handleMouseMove}
>
{this.props.render(this.state)}
</div>
);
}
}

上面的 render 函数里面调用了this.props.render(this.state)方法,父组件通过 render 属性传入一个函数,该函数返回需要渲染的元素或组件,子组件调用父组件传入的函数,传入自身的状态来渲染

class Cat extends React.Component {
render() {
const mouse = this.props.mouse;
return (
<div>
cat position is {mouse.x}, {mouse.y}
</div>
);
}
}

声明一个 Cat 组件,显示 Cat 的位置

class App extends Component {
constructor(props) {
super(props);
this.PosRender = this.PosRender.bind(this);
this.CatRender = this.CatRender.bind(this);
}
PosRender(mouse) {
return (
<div>
{mouse.x}, {mouse.y}
</div>
);
}
CatRender(mouse) {
return <Cat mouse={mouse} />;
}
render() {
return (
<div>
<Mouse render={this.CatRender} />
<Mouse render={this.PosRender} />
</div>
);
}
}

可以复用组件,父组件可以动态构建自己的渲染逻辑,prefect~