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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

React 高阶组件(HOC)

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

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

組件是 React 中代碼復用的基本單元。但有時會發現某些模式并不合適傳統組件。

含義

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

作用

* 強化組件

高階組件返回的組件,可以劫持上一層傳過來的 props,然后混入新的 props,來增強組件的功能。

1.混入 props

通過承接上層的 props,在混入自己的 props,來強化組件

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 版本應用

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

2.state 控制更新

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

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)

* 復用邏輯

高階組件可以對 react 組件進行加工處理。可以根據需求對原有的組件進行加工,可以定制專屬的 HOC。
官網例子:
例如,假設有一個 CommentList 組件,它訂閱外部數據源,用以渲染評論列表:

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

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

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 不同 - 它們在 DataSource 上調用不同的方法,且渲染不同的結果。但它們的大部分實現都是一樣的: 1.在掛載時,向 DataSource 添加一個更改偵聽器。 2.在偵聽器內部,當數據源發生變化時,調用 setState。 3.在卸載時,刪除偵聽器。
所以調用函數 withSubscription 函數進行處理:

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

withSubscription 函數邏輯處理:

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

* 事件監控

HOC 還可以對原有組件進行監控。比如對一些事件監控,錯誤監控,事件監聽等一系列操作。

function ClickHoc (Component){return function Wrap(props){const dom = useRef(null)useEffect(()=>{const handleClick = () => console.log('發生點擊事件')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>組件內部點擊</button></div>} } export default ()=>{return <div className='box' ><Index /><button>組件外部點擊</button></div> }

以上 🈯? 對組件內的點擊事件做一個監聽效果

* 提升渲染性能

劫持渲染是 hoc 一個特性,在 wrapComponent 包裝組件中,可以對原來的組件,進行條件渲染,節流渲染,懶加載等功能。

1.條件渲染

動態掛載組件的 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>點之后我就出現了</a><button onClick={() => setVisible()} > 卸載當前組件 </button></div>} } export default renderHOC(Index)

2.分片渲染

實現一個懶加載功能的 HOC

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

異步組件(懶加載)
配合 import 實現異步加載功能.

/* 路由懶加載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.節流渲染

可以實現對業務組件的渲染控制,減少渲染次數,從而達到優化性能的效果

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(`當前組件是否渲染`,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,而高階組件是將組件轉換成另一個組件。可達到強化或節省邏輯、解決原有組件的缺陷

使用

命名格式

以 with 開頭,駝峰式命名,如 withSubscription。

注意

* 不要在 render 里使用

React 的 diff 算法(稱為協調)使用組件標識來確定它是應該更新現有子樹還是將其丟棄并掛載新子樹。如果從 render 返回的組件與前一個渲染中的組件相同(===),React 通過將子樹與新子樹進行區分來遞歸更新子樹。 如果它們不相等,則完全卸載前一個子樹。

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

每次調用 render 函數都會創建一個新的 EnhancedComponent,EnhancedComponent1 !== EnhancedComponent2. 這將導致子樹每次渲染都會進行卸載,和重新掛載的操作!

重新渲染掛載組件可能會導致改組件及所有子組件的狀態丟失

* 務必復制靜態方法

當將 HOC 應用于組件時,原始組件將使用容器組件進行包裝。這意味著新組件沒有原始組件的任何靜態方法。

1.可以手動繼承
可以手動將原始組件的靜態方法復制到 hoc 組件上來,但前提是必須準確知道應該拷貝哪些方法。

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

2.引入第三方庫
每個靜態方法都綁定會很累,尤其對于開源的 hoc,對原生組件的靜態方法是未知的,我們可以使用 hoist-non-react-statics 自動拷貝所有的靜態方法:

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

* Refs 不會被傳遞

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

* 謹慎修改原型鏈

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

這樣做會產生一些不良后果。比如如果你再用另一個同樣會修改 componentDidMount 的 HOC 增強它,那么前面的 HOC 就會失效!同時,這個 HOC 也無法應用于沒有生命周期的函數組件。

總結

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

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。