前言
定义
高阶组件就是一个函数,传给它一个组件,它返回一个新的组件。
const NewComponent = higherOrderComponent(OldComponent)
设计核心
它的设计思想很像设计模式中的装饰者模式,为任何需要某些数据或者逻辑代码的对象提供所需然后返回。有关装饰者设计模式的解读欢迎参考我的语雀专辑:装饰者模式
解决问题
主要解决数据共享或者代码逻辑共享,提高代码可复用性、可维护性。
案例一 :共享数据
非常常见的是一个系统中已经登录的用户,我们是具有一定的用户信息的,假设我们知道的用户对象信息是这样的:user:{userName:'张三',age:13}
,我们在两个组件或者说页面中都需要使用这里的数据,只不过用途不同,可以对比看下我们的写法区别。
class UserInfo extends Component{
constructor(props){
super(props);
this.state = {
userName:''
}
}
componentDidMount(){
let user = localStorage.getItem('user') ;
if(user){
let userInfo = JSON.parse(user);
let {userName} = userInfo
this.setState({
userName
})
}
render(){
let {userName} = this.state ;
return (<p>用户名:{userName}</p>)
}
}
}
class UserInfoChange extends Component{
constructor(props){
super(props);
this.state = {
userName:''
}
}
componentDidMount(){
let user = localStorage.getItem('user') ;
if(user){
let userInfo = JSON.parse(user);
let {userName} = userInfo
this.setState({
userName
})
}
render(){
let {userName} = this.state ;
return (<input name='userName' defaultValue={userName}/>)
}
}
}
优化之后,我们只需要把这部分获取用户信息拿出来即可,然后通过属性传入需要的这个数据的组件,新建一个withUser.js
export default (WrappedComponent,name){
class NewComponent extends Component {
constructor () {
super() ;
this.state = { data: null }
}
componentWillMount () {
let data = localStorage.getItem(name) ;
this.setState({ data })
}
render () {
return (<WrappedComponent data={this.state.data}>)
}
}
return NewComponent
}
那么我们原来的用户信息的组件就会变得轻便很多。
import withUser from './withUser'
class UserInfoWithUser extends Component{
constructor(props){
super(props);
this.state = {
}
}
render(){
let {user} = this.props ;
return (<p>用户名:{user.userName}</p>)
}
}
}
UserInfoWithUser = withUser(UserInfoWithUser, 'user')
export default UserInfoWithUser
案例二 :共享某些业务逻辑
方式也是一样的,主要是可以将某些组件中可以共用的方法(业务逻辑或者工具方法)提炼到另外的函数中。具体案例略。
多层高阶组件
试图理解下下面的图示,假如我们的需求是从localStorage中获取数据后,属性传入一个组件, 然后再根据ajax,再属性传入一个组件,那么就会形成一个多层的高阶组件。
它的具体写法可能会是这样的:要格外注意其包裹的顺序哦。
import wrapWithLoadData from './wrapWithLoadData'
import wrapWithAjaxData from './wrapWithAjaxData'
class InputWithUserName extends Component {
render () {
return <input value={this.props.data} />
}
}
InputWithUserName = wrapWithAjaxData(InputWithUserName)
InputWithUserName = wrapWithLoadData(InputWithUserName, 'username')
export default InputWithUserName
参考文档
- 张容铭《js的设计模式》装饰者模式
- 高阶组件
- js设计模式语雀专辑