日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

React 高阶组件(HOC)

發(fā)布時(shí)間:2023/12/14 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 React 高阶组件(HOC) 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

官方文檔:https://zh-hans.reactjs.org/docs/higher-order-components.html

組件是 React 中代碼復(fù)用的基本單元。但有時(shí)會(huì)發(fā)現(xiàn)某些模式并不合適傳統(tǒng)組件。

含義

HOC 是靈活使用 react 組件的一個(gè)技巧,本身不是一個(gè)組件,是一個(gè)參數(shù)為組件,返回值是一個(gè)組件的函數(shù)。
HOC 本身不是 react 中 API 的一部分,是 react 的組合特性而形成的設(shè)計(jì)模式。

作用

* 強(qiáng)化組件

高階組件返回的組件,可以劫持上一層傳過(guò)來(lái)的 props,然后混入新的 props,來(lái)增強(qiáng)組件的功能。

1.混入 props

通過(guò)承接上層的 props,在混入自己的 props,來(lái)強(qiáng)化組件

function classHOC(WrapComponent){return class Idex extends React.Component{state={name:'gs'}componentDidMount(){console.log('HOC')}render(){return <WrapComponent{ ...this.props }{...this.state }/>}} } function Index(props){const { name,...otherProps } = propsuseEffect(()=>{console.log( 'index' )},[])return <div>my name is { name }</div> }export default classHOC(Index)

hooks 版本應(yīng)用

function functionHoc(WrapComponent){return function Index(props){const [ state , setState ] = useState({ name :'gs' })return <WrapComponent { ...props } { ...state } />} }

2.state 控制更新

可以將 HOC 的 state 的配合起來(lái),控制業(yè)務(wù)組件的更新。類似用于處理來(lái)自 react-redux 中 state 更改,帶來(lái)的訂閱更新的作用。

function classHOC(WrapComponent){return class Idex extends React.Component{constructor(){super()this.state={name:'gs'}}const changeName=(name)=>{this.setState({ name })}render(){return<WrapComponent{ ...this.props }{ ...this.state }changeName={this.changeName}/>}} } function Index(props){const [ value ,setValue ] = useState(null)const { name ,changeName } = propsreturn <div><div> hello,world , my name is { name }</div>改變name <input onChange={ (e)=> setValue(e.target.value) } /><button onClick={ ()=> changeName(value) } >確定</button></div> }export default classHOC(Index)

* 復(fù)用邏輯

高階組件可以對(duì) react 組件進(jìn)行加工處理。可以根據(jù)需求對(duì)原有的組件進(jìn)行加工,可以定制專屬的 HOC。
官網(wǎng)例子:
例如,假設(shè)有一個(gè) CommentList 組件,它訂閱外部數(shù)據(jù)源,用以渲染評(píng)論列表:

class CommentList extends React.Component {constructor(props) {super(props);this.handleChange = this.handleChange.bind(this);this.state = {// 假設(shè) "DataSource" 是個(gè)全局范圍內(nèi)的數(shù)據(jù)源變量comments: DataSource.getComments()};}componentDidMount() {// 訂閱更改DataSource.addChangeListener(this.handleChange);}componentWillUnmount() {// 清除訂閱DataSource.removeChangeListener(this.handleChange);}handleChange() {// 當(dāng)數(shù)據(jù)源更新時(shí),更新組件狀態(tài)this.setState({comments: DataSource.getComments()});}render() {return (<div>{this.state.comments.map((comment) => (<Comment comment={comment} key={comment.id} />))}</div>);} }

編寫了一個(gè)用于訂閱單個(gè)博客帖子的組件,該帖子遵循類似的模式:

class BlogPost extends React.Component {constructor(props) {super(props);this.handleChange = this.handleChange.bind(this);this.state = {blogPost: DataSource.getBlogPost(props.id)};}componentDidMount() {DataSource.addChangeListener(this.handleChange);}componentWillUnmount() {DataSource.removeChangeListener(this.handleChange);}handleChange() {this.setState({blogPost: DataSource.getBlogPost(this.props.id)});}render() {return <TextBlock text={this.state.blogPost} />;} }

CommentList 和 BlogPost 不同 - 它們?cè)?DataSource 上調(diào)用不同的方法,且渲染不同的結(jié)果。但它們的大部分實(shí)現(xiàn)都是一樣的: 1.在掛載時(shí),向 DataSource 添加一個(gè)更改偵聽(tīng)器。 2.在偵聽(tīng)器內(nèi)部,當(dāng)數(shù)據(jù)源發(fā)生變化時(shí),調(diào)用 setState。 3.在卸載時(shí),刪除偵聽(tīng)器。
所以調(diào)用函數(shù) withSubscription 函數(shù)進(jìn)行處理:

const CommentListWithSubscription = withSubscription(CommentList,(DataSource) => DataSource.getComments() );const BlogPostWithSubscription = withSubscription(BlogPost,(DataSource, props) => DataSource.getBlogPost(props.id) );

withSubscription 函數(shù)邏輯處理:

// 此函數(shù)接收一個(gè)組件... function withSubscription(WrappedComponent, selectData) {// ...并返回另一個(gè)組件...return class extends React.Component {constructor(props) {super(props);this.handleChange = this.handleChange.bind(this);this.state = {data: selectData(DataSource, props)};}componentDidMount() {// ...負(fù)責(zé)訂閱相關(guān)的操作...DataSource.addChangeListener(this.handleChange);}componentWillUnmount() {DataSource.removeChangeListener(this.handleChange);}handleChange() {this.setState({data: selectData(DataSource, this.props)});}render() {// ... 并使用新數(shù)據(jù)渲染被包裝的組件!// 請(qǐng)注意,我們可能還會(huì)傳遞其他屬性return <WrappedComponentdata={this.state.data}{...this.props}/>;}}; }

* 事件監(jiān)控

HOC 還可以對(duì)原有組件進(jìn)行監(jiān)控。比如對(duì)一些事件監(jiān)控,錯(cuò)誤監(jiān)控,事件監(jiān)聽(tīng)等一系列操作。

function ClickHoc (Component){return function Wrap(props){const dom = useRef(null)useEffect(()=>{const handleClick = () => console.log('發(fā)生點(diǎn)擊事件')dom.current.addEventListener('click',handleClick)return () => dom.current.removeEventListener('click',handleClick)},[])return <div ref={dom} ><Component {...props} /></div>} }@ClickHoc class Index extends React.Component{render(){return <div className='index' ><button>組件內(nèi)部點(diǎn)擊</button></div>} } export default ()=>{return <div className='box' ><Index /><button>組件外部點(diǎn)擊</button></div> }

以上 🈯? 對(duì)組件內(nèi)的點(diǎn)擊事件做一個(gè)監(jiān)聽(tīng)效果

* 提升渲染性能

劫持渲染是 hoc 一個(gè)特性,在 wrapComponent 包裝組件中,可以對(duì)原來(lái)的組件,進(jìn)行條件渲染,節(jié)流渲染,懶加載等功能。

1.條件渲染

動(dòng)態(tài)掛載組件的 HOC

import {SyncOutlined} from '@ant-design/icons' function renderHOC(WrapComponent){return class Index extends React.Component{constructor(props){super(props)this.state={ visible:false }}const setVisible=()=>{this.setState({ visible:!this.state.visible })}render(){const { visible } = this.statereturn <div className="box" ><button onClick={ this.setVisible } > 掛載組件 </button>{ visible ? <WrapComponent { ...this.props } setVisible={ this.setVisible } />: <div ><SyncOutlined spin className="theicon" /></div> }</div>}} }class Index extends React.Component{render(){const { setVisible } = this.propsreturn <div className="box" ><a>點(diǎn)之后我就出現(xiàn)了</a><button onClick={() => setVisible()} > 卸載當(dāng)前組件 </button></div>} } export default renderHOC(Index)

2.分片渲染

實(shí)現(xiàn)一個(gè)懶加載功能的 HOC

const renderQueue = [] let isFirstRender = falseconst tryRender = ()=>{const render = renderQueue.shift()if(!render) returnsetTimeout(()=>{render() /* 執(zhí)行下一段渲染 */},300) } /* HOC */ function renderHOC(WrapComponent){return function Index(props){const [ isRender , setRender ] = useState(false)useEffect(()=>{renderQueue.push(()=>{ /* 放入待渲染隊(duì)列中 */setRender(true)})if(!isFirstRender) {tryRender()isFirstRender = true}},[])return isRender ? <WrapComponent tryRender={tryRender} { ...props } /> : <div className='box' ><div className="icon" ><SyncOutlined spin /></div></div>} } /* 業(yè)務(wù)組件 */ class Index extends React.Component{componentDidMount(){const { name , tryRender} = this.props/* 上一部分渲染完畢,進(jìn)行下一部分渲染 */tryRender()console.log( name+'渲染')}render(){return <div><a>我就出現(xiàn)了</a></div>} } /* 高階組件包裹 */ const Item = renderHOC(Index)export default () => {return <React.Fragment><Item name="組件一" /><Item name="組件二" /><Item name="組件三" /></React.Fragment> }

異步組件(懶加載)
配合 import 實(shí)現(xiàn)異步加載功能.

/* 路由懶加載HOC */ export default function AsyncRouter(loadRouter) {return class Content extends React.Component {state = {Component: null}componentDidMount() {if (this.state.Component) returnloadRouter().then(module => module.default).then(Component => this.setState({Component},))}render() {const {Component} = this.statereturn Component ? <Component {...this.props}/> : null}} }

使用

const Index = AsyncRouter(()=>import('../pages/index'))

3.節(jié)流渲染

可以實(shí)現(xiàn)對(duì)業(yè)務(wù)組件的渲染控制,減少渲染次數(shù),從而達(dá)到優(yōu)化性能的效果

function HOC (Component){return function renderWrapComponent(props){const { num } = propsconst RenderElement = useMemo(() => <Component {...props} /> ,[ num ])return RenderElement} } class Index extends React.Component{render(){console.log(`當(dāng)前組件是否渲染`,this.props)return <div>hello,world, my name is alien </div>} } const IndexHoc = HOC(Index)export default ()=> {const [ num ,setNumber ] = useState(0)const [ num1 ,setNumber1 ] = useState(0)const [ num2 ,setNumber2 ] = useState(0)return <div><IndexHoc num={ num } num1={num1} num2={ num2 } /><button onClick={() => setNumber(num + 1) } >num++</button><button onClick={() => setNumber1(num1 + 1) } >num1++</button><button onClick={() => setNumber2(num2 + 1) } >num2++</button></div> }

意義

組件是把 props 渲染成 UI,而高階組件是將組件轉(zhuǎn)換成另一個(gè)組件。可達(dá)到強(qiáng)化或節(jié)省邏輯、解決原有組件的缺陷

使用

命名格式

以 with 開(kāi)頭,駝峰式命名,如 withSubscription。

注意

* 不要在 render 里使用

React 的 diff 算法(稱為協(xié)調(diào))使用組件標(biāo)識(shí)來(lái)確定它是應(yīng)該更新現(xiàn)有子樹(shù)還是將其丟棄并掛載新子樹(shù)。如果從 render 返回的組件與前一個(gè)渲染中的組件相同(===),React 通過(guò)將子樹(shù)與新子樹(shù)進(jìn)行區(qū)分來(lái)遞歸更新子樹(shù)。 如果它們不相等,則完全卸載前一個(gè)子樹(shù)。

render() {const EnhancedComponent = enhance(MyComponent);return <EnhancedComponent />; }

每次調(diào)用 render 函數(shù)都會(huì)創(chuàng)建一個(gè)新的 EnhancedComponent,EnhancedComponent1 !== EnhancedComponent2. 這將導(dǎo)致子樹(shù)每次渲染都會(huì)進(jìn)行卸載,和重新掛載的操作!

重新渲染掛載組件可能會(huì)導(dǎo)致改組件及所有子組件的狀態(tài)丟失

* 務(wù)必復(fù)制靜態(tài)方法

當(dāng)將 HOC 應(yīng)用于組件時(shí),原始組件將使用容器組件進(jìn)行包裝。這意味著新組件沒(méi)有原始組件的任何靜態(tài)方法。

1.可以手動(dòng)繼承
可以手動(dòng)將原始組件的靜態(tài)方法復(fù)制到 hoc 組件上來(lái),但前提是必須準(zhǔn)確知道應(yīng)該拷貝哪些方法。

function HOC(Component) {class WrappedComponent extends React.Component {/*...*/}// 必須準(zhǔn)確知道應(yīng)該拷貝哪些方法WrappedComponent.staticMethod = Component.staticMethodreturn WrappedComponent }

2.引入第三方庫(kù)
每個(gè)靜態(tài)方法都綁定會(huì)很累,尤其對(duì)于開(kāi)源的 hoc,對(duì)原生組件的靜態(tài)方法是未知的,我們可以使用 hoist-non-react-statics 自動(dòng)拷貝所有的靜態(tài)方法:

import hoistNonReactStatic from 'hoist-non-react-statics' function HOC(Component) {class WrappedComponent extends React.Component {/*...*/}hoistNonReactStatic(WrappedComponent,Component)return WrappedComponent }

* Refs 不會(huì)被傳遞

雖然高階組件的約定是將所有 props 傳遞給被包裝組件,但這對(duì)于 refs 并不適用。那是因?yàn)?ref 實(shí)際上并不是一個(gè) prop, - 就像 key 一樣,它是由 React 專門處理的。如果將 ref 添加到 HOC 的返回組件中,則 ref 引用指向容器組件,而不是被包裝組件。

* 謹(jǐn)慎修改原型鏈

function HOC (Component){const proDidMount = Component.prototype.componentDidMountComponent.prototype.componentDidMount = function(){console.log('劫持生命周期:componentDidMount')proDidMount.call(this)}return Component }

這樣做會(huì)產(chǎn)生一些不良后果。比如如果你再用另一個(gè)同樣會(huì)修改 componentDidMount 的 HOC 增強(qiáng)它,那么前面的 HOC 就會(huì)失效!同時(shí),這個(gè) HOC 也無(wú)法應(yīng)用于沒(méi)有生命周期的函數(shù)組件。

總結(jié)

以上是生活随笔為你收集整理的React 高阶组件(HOC)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。