亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

React使用Context的一些優(yōu)化建議

 更新時間:2024年04月10日 08:57:57   作者:奇舞精選  
Context?提供了一個無需為每層組件手動添加?props,就能在組件樹間進行數(shù)據(jù)傳遞的方法,本文為大家整理了React使用Context的一些優(yōu)化建議,希望對大家有所幫助

常用 API

React.createContext

const MyContext = React.createContext(defaultValue);

創(chuàng)建一個 Context 對象。當 React 渲染一個訂閱了這個 Context 對象的組件,這個組件會從組件樹中離自身最近的那個匹配的 Provider 中讀取到當前的 context 值。

Context.Provider

<MyContext.Provider value={/* 某個值 */}>

每個 Context 對象都會返回一個 Provider React 組件,它允許消費組件訂閱 context 的變化。

Provider 接收一個 value 屬性,傳遞給消費組件。一個 Provider 可以和多個消費組件有對應關系。多個 Provider 也可以嵌套使用,里層的會覆蓋外層的數(shù)據(jù)。

useContext

const store = useContext(MyContext)

接收一個 context 對象(React.createContext 的返回值)并返回該 context 的當前值。當前的 context 值由上層組件中距離當前組件最近的 <MyContext.Provider> 的 value prop 決定。

當 Provider 的 value 值發(fā)生變化時,它內部的所有消費組件都會重新渲染

了解了 API 后,我們來看一個簡單的例子。

示例

index.js

const MyContext = React.createContext(null);

function reducer(state, action) {
  switch (action.type) {
    case 'addCount': {
      return {
        ...state,
        count: state.count + 1
      }
    }
    case 'addNum': {
      return {
        ...state,
        num: state.num + 1
      }
    }
    default: return state;
  }
}

const MyProvider = ({ children }) => {
  const [store, dispatch] = useReducer(reducer, { count: 0, num: 0 })
  return <MyContext.Provider value={{store, dispatch}}>{children}</MyContext.Provider>
};

export default () => {
  return (
    <MyProvider>
      <ChildCount />
      <ChildNum />
      <Child  />
    </MyProvider>
  );
}

ChildCount.js

export default () => {
    const { state, dispatch } = React.useContext(MyContext);
    console.log('re-render ChildCount', state.count)
    return (
      <>
        <div>count is: {state.count}</div>
        <button onClick={() => dispatch({ type: 'addCount' })}> AddCount </button>
      </>
    )
}

ChildNum.js

export default () => {
    const { state, dispatch } = React.useContext(MyContext);
    console.log('re-render ChildNum', state.num)
    return (
      <>
        <div>num is: {state.num}</div>
        <button onClick={() => dispatch({ type: 'addNum' })}> AddNum </button>
      </>
    )
}

Child.js

export default () => {
  console.log('re-render Child')
  return <div>Child</div>
}

點擊 AddCount 按鈕,輸出:

re-render ChildCount 1re-render ChildNum 0

點擊 AddNum 按鈕,輸出:

re-render ChildCount 1re-render ChildNum 1

我們可以發(fā)現(xiàn),Context.Provider 下的所有消費組件,在 Provider.value 變化后,都會 re-render

改變 count 、num 任意一個值,ChildCount,ChildNum 都會 re-render

針對以上 re-render 情況,有以下方案可以優(yōu)化

優(yōu)化

針對子組件做函數(shù)記憶

React.memo

我們如下修改所有的 Child 組件

export default React.memo(() => {
    const { state, dispatch } = React.useContext(MyContext);
    console.log('re-render ChildCount', state.count)
    return (
      <>
        <div>count is: {state.count}</div>
        <button onClick={() => dispatch({ type: 'addCount' })}> AddCount </button>
      </>
    )
})

點擊 AddCount 后發(fā)現(xiàn),依然打印出

re-render ChildCount 1
re-render ChildNum 0

我們重新認識下 React.memo

React.memo 默認情況下僅僅對傳入的 props 做淺比較,如果是內部自身狀態(tài)更新(useState, useContext等),依然會重新渲染,在上面的例子中,useContext 返回的 state 一直在變化,導致就算被 memo 包裹的組件依然觸發(fā)更新了。

useMemo

我們如下修改所有的 Child 組件

export default () => {
    const { state, dispatch } = React.useContext(MyContext);
    return useMemo(() => {
      console.log('re-render ChildCount', state.count)
      return (
          <>
            <div>count is: {state.count}</div>
            <button onClick={() => dispatch({ type: 'addCount' })}> AddCount </button>
          </>
      )
    }, [state.count, dispatch])
}

點擊 addCount 后發(fā)現(xiàn),只打印出了

re-render ChildCount 1

點擊 addNum 后發(fā)現(xiàn),只打印出了

re-render ChildNum 1

useMemo 可以做更細粒度的緩存,我們可以在依賴數(shù)組里來管理組件是否更新

我們可以思考一下,有沒有一種辦法,不用 useMemo 也可以做到按需渲染。就像 react-redux 中 useSelector 一樣實現(xiàn)按需渲染

動手實現(xiàn) useSelector

我們先想一下,在上面的例子中,觸發(fā)子組件re-render的原因是什么?

沒錯就是因為 Provider.value 的值一直在變更,那我們要想個辦法讓子組件感知不到 value 的變更,同時在 value 的某個值發(fā)生變更的時候,能夠觸發(fā)消費 value 的子組件 re-render

我們使用 觀察者模式 實現(xiàn)

1、我們使用 useMemo 緩存首次的 value,讓子組件感知不到 value 的變化

2、如果 value 不變化,那子組件就不會re-render,此時我們需要在真正 value 變化的時候,re-render 子組件,我們需要一個 hooks(useSelector) 幫助我們實現(xiàn)子組件 re-render

3、子組件在初始化時,useSelector 要幫助其訂閱 state 變更的回調函數(shù),并返回最新的 state(函數(shù)內部獲取前后兩次的 state 做對比,不一樣則強制更新組件)

4、在 Context.Provider 中創(chuàng)建一個收集子組件訂閱state變更回調的集合,在其內部監(jiān)聽 state(value),如果變更則遍歷集合,執(zhí)行所有回調函數(shù)

基于以上,我們依次實現(xiàn)了Context.Provider, useSelector, useDispatch

Context.Provider

const MyProvider = ({children}) => {
  const [state, dispatch] = useReducer(reducer, initState);
  
  // ref state
  const stateRef = useRef(null);
  stateRef.current = state;

  // ref 訂閱回調數(shù)組
  const subscribersRef = useRef([]);

  // state 變化,遍歷執(zhí)行回調
  useEffect(() => {
    subscribersRef.current.forEach(sub => sub());
  }, [state]);

  // 緩存 value, 利用 ref 拿到最新的 state, subscribe 狀態(tài)
  const value = useMemo(
    () => ({
      dispatch,
      subscribe: cb => {
        subscribersRef.current.push(cb);
        return () => {
          subscribersRef.current = subscribersRef.current.filter(item => item !== cb);
        };
      },
      getState: () => stateRef.current
    }),
    []
  )

  return <MyContext.Provider children={children} value={value} />;
}

useSelector

export const useSelector = selector => {
  // 強制更新
  const [, forceRender] = useReducer(v => v + 1, 0);
  const store = useContext(MyContext);

  // 獲取當前使用的 state
  const selectedStateRef = useRef(null)
  selectedStateRef.current = selector(store.getState());

  // 對比更新回調
  const checkForUpdates = useCallback(() => {
    // 獲取變更后的 state
    const newState = selector(store.getState());
    // 對比前后兩次 state
    if (newState !== selectedStateRef.current) forceRender({});
  }, [store]);
  
  // 訂閱 state
  useEffect(() => {
    const subscription = store.subscribe(checkForUpdates);
    return () => subscription();
  }, [store, checkForUpdates]);
  
  // 返回需要的 state
  return selectedStateRef.current;
}

useDispatch

export const useDispatch = () => {
  const store = useContext(MyContext);
  return store.dispatch
}

我們用上面重寫的 API,改寫下剛開始的例子

index.js

export default () => {
    return (
      <MyProvider>
        <ChildCount />
        <ChildNum />
        <Child />
      </Provider>
    );
}

ChildCount.js

export default () => {
    const dispatch = useDispatch();
    const count = useSelector(state => state.count);
    console.log('re-render ChildCount', count)
    return (
      <>
        <div>count is: {count}</div>
        <button onClick={() => dispatch({ type: 'addCount' });}> AddCount </button>
      </>
    )
};

ChildNum.js

export default () => {
    const dispatch = useDispatch();
    const num = useSelector(state => state.num);
    console.log('re-render ChildNum', num)
    return (
      <>
        <div>num is: {num}</div>
        <button onClick={() => dispatch({ type: 'addNum' });}> AddNum </button>
      </>
    )
}

Child.js

export default () => {
    console.log('re-render Child')
    return <div>Child</div>
}

點擊AddCount: 只打印了 re-render ChildCount 1

點擊AddNum: 只打印了 re-render ChildNum 1

以上通過對 Context 使用中的一些思考,我們簡單的實現(xiàn)了 useSelector,實現(xiàn)了 Context 組件的按需渲染

總結

在使用 Context API 的時候,要避免不必要的re-render,可以使用 useMemo 做細粒度更新,也可以使用 useSelector 實現(xiàn)按需渲染

以上就是React使用Context的一些優(yōu)化建議的詳細內容,更多關于React Context的資料請關注腳本之家其它相關文章!

相關文章

  • 詳解React中如何獲取真實的dom

    詳解React中如何獲取真實的dom

    這篇文章主要為大家詳細介紹了React中獲取真實的dom的相關方法,文中的示例代碼講解詳細,具有一定的借鑒價值,感興趣的小伙伴可以跟隨小編一起學習一下
    2025-02-02
  • react ant-design Select組件下拉框map不顯示的解決

    react ant-design Select組件下拉框map不顯示的解決

    這篇文章主要介紹了react ant-design Select組件下拉框map不顯示的解決方案,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-03-03
  • React Hooks使用方法全方位介紹

    React Hooks使用方法全方位介紹

    在react類組件(class)寫法中,有setState和生命周期對狀態(tài)進行管理,但是在函數(shù)組件中不存在這些,故引入hooks(版本:>=16.8),使開發(fā)者在非class的情況下使用更多react特性
    2023-03-03
  • React的createElement和render手寫實現(xiàn)示例

    React的createElement和render手寫實現(xiàn)示例

    這篇文章主要為大家介紹了React的createElement和render手寫實現(xiàn)示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-08-08
  • react源碼層深入刨析babel解析jsx實現(xiàn)

    react源碼層深入刨析babel解析jsx實現(xiàn)

    同作為MVVM框架,React相比于Vue來講,上手更需要JavaScript功底深厚一些,本系列將閱讀React相關源碼,從jsx -> VDom -> RDOM等一些列的過程,將會在本系列中一一講解
    2022-10-10
  • React+echarts?(echarts-for-react)?實現(xiàn)中國地圖及省份切換功能

    React+echarts?(echarts-for-react)?實現(xiàn)中國地圖及省份切換功能

    這篇文章主要介紹了React+echarts?(echarts-for-react)?畫中國地圖及省份切換,有足夠的地圖數(shù)據(jù),可以點擊到街道,示例我只出到市級,本文結合實例代碼給大家介紹的非常詳細需要的朋友可以參考下
    2022-11-11
  • 詳解在create-react-app使用less與antd按需加載

    詳解在create-react-app使用less與antd按需加載

    這篇文章主要介紹了詳解在create-react-app使用less與antd按需加載,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-12-12
  • 詳解react-router 4.0 下服務器如何配合BrowserRouter

    詳解react-router 4.0 下服務器如何配合BrowserRouter

    這篇文章主要介紹了詳解react-router 4.0 下服務器如何配合BrowserRouter,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-12-12
  • 如何使用React構建一個高效的視頻上傳組件

    如何使用React構建一個高效的視頻上傳組件

    本文介紹了如何使用React構建一個高效的視頻上傳組件,包括基礎概念、常見問題與解決方案以及易錯點,通過實際代碼案例,展示了如何實現(xiàn)文件大小和格式驗證、上傳進度顯示等功能,并詳細解釋了跨域請求和并發(fā)上傳控制等技術細節(jié)
    2025-01-01
  • react-dnd實現(xiàn)任意拖動與互換位置

    react-dnd實現(xiàn)任意拖動與互換位置

    這篇文章主要為大家詳細介紹了react-dnd實現(xiàn)任意拖動與互換位置,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-08-08

最新評論