高阶组件就是一个函数,接收一个组件,返回一个新的组件,可以用来扩展传入的组件的功能
注:在高阶组件之前通过 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> ); }}
可以复用