前言
定義
高階元件就是一個函式,傳給它一個元件,它返回一個新的元件。
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設計模式語雀專輯