什麼是高階組件
如果一個函數,接受一個或多個組件作爲參數並且返回一個組件,就可以稱之爲高階組件。
React中的高階組件
React中的高階組件主要有兩種形式:屬性代理(Props Proxy)和反向繼承(Inheritance Inversion)。
1.屬性代理
分兩種情況:
情況一:無狀態組件(Stateless Component)
function MapComponent(WrappedComponent) {
return <WrappedComponent />
}
情況二:有狀態組件
const MapComponent = WrappedComponent => {
return class extends React.Component {
render() {
return (
<WrappedComponent {...this.props} />
)
}
}
}
屬性代理是:一個函數接受一個 WrappedComponent 組件作爲參數傳入,並返回一個繼承了 React.Component 組件的類,且在該類的 render() 方法中返回被傳入的 WrappedComponent 組件。
React.Component能做什麼,這個高階組件就能做什麼:
1).操作props
const MapComponent = WrappedComponent => {
return class extends React.Component {
render() {
// 將props的view變爲null
const newProps = {
view: null
}
return (
<>
<WrappedComponent {...this.props} {...newProps} />
</>
)
}
}
}
2).操作state
//利用props和回調函數
const InputComponent = (WrappedComponent) => {
return class extends React.Component{
constructor(props) {
super(props);
this.state = {
name: '',
};
}
onChange = () => {
this.setState({
name: '這是一個文本框',
});
}
render() {
const newProps = {
name: {
value: this.state.name,
onChange: this.onChange,
},
};
return <WrappedComponent {...this.props} {...newProps} />;
}
};
}
//使用
const NameInput = props => (<input name="name" {...props.name} />);
export default InputComponent(NameInput);
3).狀態判斷:符合條件時才渲染傳入的組件
// view初始化加載需要一定時間,因爲作爲一個判斷條件,當view裏有值時,才渲染組件
const MapComponent = WrappedComponent => {
return class extends React.Component {
render() {
const { view } = this.props;
return (
<>
{view ? <WrappedComponent {...this.props} /> : ''}
</>
)
}
}
}
2.反向繼承
示例:
function MapComponent(WrappedComponent) {
return class extends WrappedComponent{
render() {
return super.render();
}
};
}
反向繼承:一個函數接受一個 WrappedComponent 組件作爲參數傳入,並返回一個繼承了該傳入 WrappedComponent 組件的類,且在該類的 render() 方法中返回 super.render() 方法。
可以進行的操作:
1).操作state
高階組件中可以讀取、編輯和刪除 WrappedComponent 組件實例中的 state。但是不建議這麼做。
2).渲染劫持
操作render()輸出的React元素樹
const HigherOrderComponent = WrappedComponent => {
return class extends WrappedComponent {
render() {
const tree = super.render();
const newProps = {};
if (tree && tree.type === 'input') {
newProps.value = 'something here';
}
const props = {
...tree.props,
...newProps,
};
const newTree = React.cloneElement(tree, props, tree.props.children);
return newTree;
}
};
}
條件渲染
const UserComponent = WrappedComponent => {
return class extends React.Component {
render() {
const { user } = this.props;
if(user === 'admin') { //管理員
return <Admin />
} else { //其他用戶
return <Common />
}
}
}
}
總結:屬性代理和反向繼承都是繼承了某個父類的子類,屬性代理中繼承的是 React.Component,反向繼承中繼承的是傳入的組件 WrappedComponent。
高階組件的約定
- props 保持一致
- 你不能在函數式(無狀態)組件上使用 ref 屬性,因爲它沒有實例
- 不要以任何方式改變原始組件WrappedComponent
- 透傳不相關 props 屬性給被包裹的組件 WrappedComponent
- 不要再 render()方法中使用高階組件
- 使用 compose 組合高階組件
- 包裝顯示名字以便於調試
應用場景
1)系統的權限控制
const UserComponent = WrappedComponent => {
return class extends React.Component {
render() {
const { user } = this.props;
if(user === 'admin') { //管理員
return <Admin />
} else { //其他用戶
return "您沒有權限查看該頁面!"
}
}
}
}
2).頁面複用
把一個功能相同的部分封裝爲高階組件,這樣當多次使用時只需要調用高階組件即可,從而實現頁面的複用。
裝飾者模式
定義:在不改變對象自身的前提下在程序運行期間動態的給對象添加一些額外的屬性或行爲。
因此,高階組件就是裝飾者模式在React中的實現:通過給函數傳入一個組件(函數或類)後在函數內部對該組件(函數或類)進行功能的增強(不修改傳入參數的前提下),最後返回這個組件(函數或類),即允許向一個現有的組件添加新的功能,同時又不去修改該組件。