React用法之高階組件的用法詳解
監(jiān)介
高階組件也就是我們常說的HOC,是React中用于復(fù)用組件邏輯的一種高級(jí)技巧。HOC自身不是React API的一部分,他是一種基于React的組合特性而形成的設(shè)計(jì)模式。
其實(shí)就是組件作為參數(shù),返回值也是組件的函數(shù),他是純函數(shù),不會(huì)修改傳入的組件,也不會(huì)使用繼承來復(fù)制其行為。相反,HOC通過將組件包裝在容器組件中來組成新組件。HOC是純函數(shù),沒有副作用。
使用HOC的原因
- 抽取重復(fù)代碼,實(shí)現(xiàn)組件復(fù)用:相同功能組件復(fù)用
- 條件渲染,控制組件的渲染邏輯(渲染劫持):權(quán)限控制
- 捕獲/劫持被處理組件的生命周期,常見場(chǎng)景:組件渲染性能追蹤、日志打點(diǎn)。
HOC的實(shí)現(xiàn)方式
先來看一個(gè)簡(jiǎn)單的HOC實(shí)現(xiàn)方式
function withLog(Component) {
return function (props) {
console.log(`[${new Date().toISOString()}] ${Component.name} props:`, props);
return <Component {...props} />;
};
}這個(gè)HOC接收一個(gè)組件作為參數(shù),返回一個(gè)新的組件,新的組件在渲染之前會(huì)先輸出當(dāng)前時(shí)間和傳入的組件的名稱和props到控制臺(tái),然后再將props傳遞給原始組件進(jìn)行渲染。
使用這個(gè)HOC可以像下面這樣
function MyComponent(props) {
return <div>{props.message}</div>;
}
const MyComponentWithLog = withLog(MyComponent);
ReactDOM.render(
<MyComponentWithLog message="Hello, world!" />,
document.getElementById('root')
);渲染的時(shí)候控制臺(tái)會(huì)輸出
[2023-04-16T00:00:00.000Z] MyComponent props: { message: "Hello, world!" }
可以看到,HOC 可以用來增強(qiáng)組件的功能,比如添加日志、添加權(quán)限校驗(yàn)、添加數(shù)據(jù)預(yù)處理等。
其實(shí)HOC的實(shí)現(xiàn)方式主要分為兩種:屬性代理和反向繼承
屬性代理
使用組合的方式,將組件包裝在容器上,依賴父子組件的生命周期關(guān)系來:
- 返回?zé)o狀態(tài)的函數(shù)組件
- 返回class組件
操作props
// 可以通過屬性代理,攔截父組件傳遞過來的porps并進(jìn)行處理。
// 返回一個(gè)無狀態(tài)的函數(shù)組件
function HOC(WrappedComponent) {
const newProps = { type: 'HOC' };
return props => <WrappedComponent {...props} {...newProps}/>;
}
// 返回一個(gè)有狀態(tài)的 class 組件
function HOC(WrappedComponent) {
return class extends React.Component {
render() {
const newProps = { type: 'HOC' };
return <WrappedComponent {...this.props} {...newProps}/>;
}
};
}抽象state
通過屬性代理無法直接操作原組件的state,可以通過props和回調(diào)函數(shù)抽象state
function HOC(WrappedComponent) {
return class extends React.Component {
constructor(props) {
super(props);
this.state = {
name: '',
};
this.onChange = this.onChange.bind(this);
}
onChange = (event) => {
this.setState({
name: event.target.value,
})
}
render() {
const newProps = {
name: {
value: this.state.name,
onChange: this.onChange,
},
};
return <WrappedComponent {...this.props} {...newProps} />;
}
};
}
// 使用
@HOC
class Example extends Component {
render() {
return <input name="name" {...this.props.name} />;
}
}這里我們稍微做一下解釋。我們主要的目的是想通過傳給HOC的這個(gè)組件來修改HOC的state,在HOC中定義了onChange方法目的是可以修改state。如果在WrappedComponent中調(diào)用onChange方法呢?我們可以定義一個(gè)對(duì)象作為WrappedComponent的props,然后將onChange方法存儲(chǔ)在定義的對(duì)象上,傳給WrappedComponent。
在WrappedComponent中,我們定義了一個(gè)input,我們都知道input有value屬性作為input的值,還有一個(gè)onChange方法作為change事件,我們把傳給WrappedComponent的props結(jié)構(gòu)剛好就是value和onChange。這樣當(dāng)input在change的時(shí)候就可以就修改state的值了。
通過props實(shí)現(xiàn)條件渲染
通過props來控制是否渲染及傳入數(shù)據(jù)
import * as React from 'react';
function HOC (WrappedComponent) {
return (props) => (
<div>
{
props.isShow ? (
<WrappedComponent
{...props}
/>
) : <div>暫無數(shù)據(jù)</div>
}
</div>
);
}
export default HOC;
其他元素wrapper傳入的組件
function withBackgroundColor(WrappedComponent) {
return class extends React.Component {
render() {
return (
<div style={{ backgroundColor: '#ccc' }}>
<WrappedComponent {...this.props} {...newProps} />
</div>
);
}
};
}反向繼承
使用一個(gè)函數(shù)接收一個(gè)組件作為參數(shù)傳入,并返回一個(gè)繼承了該傳入組件的類組件,且在返回組件的render()方法中返回了super.render()方法。
const HOC = (WrappedComponent) => {
return class extends WrappedComponent {
render() {
return super.render()
}
}
}- 允許HOC通過this訪問到原組件,可以直接讀取和操作原組件的state/ref等
- 可以通過
render.super()獲取傳入組件的render,可以有選擇的渲染劫持 - 劫持原組件生命周期方法
function HOC(WrappedComponent){
const didMount = WrappedComponent.prototype.componentDidMount;
// 繼承了傳入組件
return class HOC extends WrappedComponent {
async componentDidMount(){
// 劫持 WrappedComponent 組件的生命周期
if (didMount) {
await didMount.apply(this);
}
...
}
render(){
//使用 super 調(diào)用傳入組件的 render 方法
return super.render();
}
}
}讀取/操作原組件的state
function HOC(WrappedComponent){
const didMount = WrappedComponent.prototype.componentDidMount;
// 繼承了傳入組件
return class HOC extends WrappedComponent {
async componentDidMount(){
if (didMount) {
await didMount.apply(this);
}
// 將 state 中的 number 值修改成 2
this.setState({ number: 2 });
}
render(){
//使用 super 調(diào)用傳入組件的 render 方法
return super.render();
}
}
}通過this.setState({ number: 2 });修改了原組件WrappedComponent中的state的number值
條件渲染
const HOC = (WrappedComponent) =>
class extends WrappedComponent {
render() {
if (this.props.isRender) {
return super.render();
} else {
return <div>暫無數(shù)據(jù)</div>;
}
}
}修改react樹
// 修改返回render結(jié)果
function 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;
}
};
}這里首先通過super.render()拿到需要渲染的樹,然后對(duì)這個(gè)渲染樹做了修改。比如如果是一個(gè)input,則修改它的value值。
屬性代理和反向繼承對(duì)比
- 屬性代理:從"組合"角度出發(fā),有利于從外部操作WrappedComponent,可以操作props,或者在WrappedComponent外加一些攔截器(如:條件渲染,增加外部樣式)
- 反向繼承:從"繼承"角度出發(fā),從內(nèi)部操作WrappedComponent,可以操作組件內(nèi)部的state,生命周期和render等,功能更強(qiáng)大
到此這篇關(guān)于React用法之高階組件的用法詳解的文章就介紹到這了,更多相關(guān)React高階組件內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Input標(biāo)簽自動(dòng)校驗(yàn)功能去除實(shí)現(xiàn)
這篇文章主要為大家介紹了Input標(biāo)簽的自動(dòng)拼寫檢查功能去除實(shí)現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-07-07
react 不用插件實(shí)現(xiàn)數(shù)字滾動(dòng)的效果示例
這篇文章主要介紹了react 不用插件實(shí)現(xiàn)數(shù)字滾動(dòng)的效果示例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-04-04
react 可拖拽進(jìn)度條的實(shí)現(xiàn)
本文主要介紹了react 可拖拽進(jìn)度條的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-04-04
React創(chuàng)建組件的三種方式及其區(qū)別是什么
在React中,創(chuàng)建組件的三種主要方式是函數(shù)式組件、類組件和使用React Hooks的函數(shù)式組件,本文就詳細(xì)的介紹一下如何使用,感興趣的可以了解一下2023-08-08
React router cache route實(shí)現(xiàn)緩存頁(yè)面流程介紹
react-router自身沒有路由緩存的特性,在5.x版本之前,我們可以基于react-router-cache-route來實(shí)現(xiàn)路由緩存功能。但是react-router 6.x在實(shí)現(xiàn)上做了比較大的變化,react-router-cache-route沒有提供相應(yīng)的支持2023-01-01
React Fragment 和空標(biāo)簽(<></>)用法及區(qū)別詳解
本文詳細(xì)介紹了React中的Fragment和空標(biāo)簽的使用,包括它們的區(qū)別、使用場(chǎng)景、性能考慮以及最佳實(shí)踐,本文給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧2025-01-01
基于React的狀態(tài)管理實(shí)現(xiàn)一個(gè)簡(jiǎn)單的顏色轉(zhuǎn)換器
這篇文章主要介紹了用React的狀態(tài)管理,簡(jiǎn)簡(jiǎn)單單實(shí)現(xiàn)一個(gè)顏色轉(zhuǎn)換器,文中有詳細(xì)的代碼示例供大家參考,具有一定的參考價(jià)值,需要的朋友可以參考下2023-08-08

