首先说一下,不会这个也可以完成很多需求,但是拥有这个思维模式后,相信你写的代码就更加优雅,。高阶组件是学习 React 的一大难点,我开始也卡了很久,被绕晕,理解不了,但是写过几次后就觉得它的设计是如此精巧,让人叹服。
高阶组件的定义
不知道你用过高阶函数没,其实高阶组件是一个套路,就是把函数换成了组件。 翻译:高阶组件就是一个函数,它接收一个组件作为参数,并且返回一个新的组件。
我们定义了两个简单的组件来分别显示用户名和年龄,如下可以显示我们需要的
// 显示用户名的组件
const ShowName = () => (
<div>{localStorage.getItem('username')}</div>
)
// 显示年龄的组件
const ShowAge = () => (
<div>{localStorage.getItem('age')}</div>
)
但是现在产品说:这两个模块都要加入当前的时间信息,于是,代码就变成了这样。你看,下面的代码就有大量的冗余,我们当然可以提取显示时间的公共代码为一个组件,在父组件里直接引用子组件,这也是我在使用 React 经常使用的套路。
// username.js
class Username extends React.Component {
state = { time: new Date() }
componentDidMount() {
this.timerID = setInterval(() => this.tick(), 1000)
}
componentWillUnmount() {
clearInterval(this.timerID);
}
tick() {
this.setState({
time: new Date()
});
}
render() {
return (
<div>
<div>{localStorage.getItem('username')}</div>
<h2>{this.state.time}</h2>
</div>
)
}
}
// 显示年龄的组件 age.js
class Age extends React.Component {
state = { time: new Date() }
componentDidMount() {
this.timerID = setInterval(() => this.tick(), 1000)
}
componentWillUnmount() {
clearInterval(this.timerID);
}
tick() {
this.setState({
time: new Date()
});
}
render() {
return (
<div>
<div>{localStorage.getItem('age')}</div>
<h2>{this.state.time}</h2>
</div>
)
}
}
但是还有一种思维是极具有技巧性的,它同样可以达到组件复用甚至灵活处理不同的需求,它对我们的原组件不做任何的修改。首先,我们定义一个没有副作用的纯函数组件。于是乎,我们就可以在高阶组件封装的这一层展示我们想要的功能,如 username.js 展现时间和名字,age.js 展现时间和年龄,当然,想要什么都可以随意写,是不是感觉很爽,很舒服。
// withTimer.js 高阶组件
const WithTimer = (WrapComponent) => {
return class extends React.Component {
state = { time: new Date() }
componentDidMount() {
this.timerID = setInterval(() => this.tick(), 1000)
}
componentWillUnmount() {
clearInterval(this.timerID);
}
tick() {
this.setState({
time: new Date()
});
}
render() {
return <WrapComponent time={this.state.time} {...this.props} />;
}
}
}
// username.js
import WithTimer from './withTimer'
class Username extends React.Component {
render() {
return (
<div>
<div>{localStorage.getItem('username')}</div>
<h2>{this.props.time.toLocaleString() + 'username'}</h2>
</div>
)
}
}
export default WithTimer(Username)
// age.js
import WithTimer from './withTimer'
class Age extends React.Component {
render() {
return (
<div>
<div>{localStorage.getItem('age')}</div>
<h2>{this.props.time.toLocaleString() + 'age'}</h2>
</div>
)
}
}
export dafault WithTimer(Age)
总结
相对于 porps 属性传递给子组件,高阶组件可以让我们开发时实现更复杂的应用,这里的 demo 相对简单,逻辑不复杂,很多复杂的时候可以让我们的代码更加灵活和具有扩展性。自己仔细品一下,高阶组件就是一个纯函数,让我们把横切面的功能抽出来。
在 react-redux,ant 的form 表单中,都使用到了。它可以让我们的原组件的 props 上具有其他的属性可以扩展。 好了,更多的用法,还要自己慢慢品。
// connect是一个返回函数的函数(就是个高阶函数)
const enhance = connect(mapStateToProps, mapDispatchToProps)
// 返回的函数就是一个高阶组件,该高阶组件返回一个与Redux store
// 关联起来的新组件
const ConnectedComment = enhance(Component)
const WrappedNormalLoginForm = Form.create()(NormalLoginForm)