高阶组件就是一个函数,接收一个组件,返回一个新的组件,可以用来扩展传入的组件的功能
注:在高阶组件之前通过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
还有另一种方法,是把组件的静态方法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>
);
}
}
可以复用<Mouse />组件,父组件可以动态构建自己的渲染逻辑,prefect~