高阶组件
最后更新于:2022-04-02 05:39:22
## 前言
### 定义
> 高阶组件就是一个函数,传给它一个组件,它返回一个新的组件。
```jsx
const NewComponent = higherOrderComponent(OldComponent)
```
### 设计核心
它的设计思想很像设计模式中的装饰者模式,为任何需要某些数据或者逻辑代码的对象提供所需然后返回。有关装饰者设计模式的解读欢迎参考我的语雀专辑:[装饰者模式](https://www.yuque.com/robinson/design-patterns/ygc1bo)
### 解决问题
主要解决数据共享或者代码逻辑共享,提高代码可复用性、可维护性。
## 案例一 :共享数据
非常常见的是一个系统中已经登录的用户,我们是具有一定的用户信息的,假设我们知道的用户对象信息是这样的:`user:{userName:'张三',age:13}`,我们在两个组件或者说页面中都需要使用这里的数据,只不过用途不同,可以对比看下我们的写法区别。
```jsx
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 ()
}
}
return NewComponent
}
```
那么我们原来的用户信息的组件就会变得轻便很多。
```jsx
import withUser from './withUser'
class UserInfoWithUser extends Component{
constructor(props){
super(props);
this.state = {
}
}
render(){
let {user} = this.props ;
return (
';
用户名:{userName}
) } } } ``` ```jsx 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 () } } } ``` 优化之后,我们只需要把这部分获取用户信息拿出来即可,然后通过属性传入需要的这个数据的组件,新建一个withUser.js ```jsx 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 (用户名:{user.userName}
) } } } UserInfoWithUser = withUser(UserInfoWithUser, 'user') export default UserInfoWithUser ``` ## 案例二 :共享某些业务逻辑 方式也是一样的,主要是可以将某些组件中可以共用的方法(业务逻辑或者工具方法)提炼到另外的函数中。具体案例略。 ## 多层高阶组件 试图理解下下面的图示,假如我们的需求是从localStorage中获取数据后,属性传入一个组件, 然后再根据ajax,再属性传入一个组件,那么就会形成一个多层的高阶组件。 ![链接](https://user-gold-cdn.xitu.io/2019/3/18/1698fcb6a7530da3?w=1152&h=653&f=png&s=167165) 它的具体写法可能会是这样的:要格外注意其包裹的顺序哦。 ~~~ import wrapWithLoadData from './wrapWithLoadData' import wrapWithAjaxData from './wrapWithAjaxData' class InputWithUserName extends Component { render () { return } } InputWithUserName = wrapWithAjaxData(InputWithUserName) InputWithUserName = wrapWithLoadData(InputWithUserName, 'username') export default InputWithUserName ~~~ ## 参考文档 - 张容铭《js的设计模式》装饰者模式 - [高阶组件](http://huziketang.mangojuice.top/books/react/lesson28) - [js设计模式语雀专辑](https://www.yuque.com/robinson/design-patterns)