iT邦幫忙

2021 iThome 鐵人賽

DAY 29
0
自我挑戰組

30天CSS、JS、React打造專案零組件系列 第 29

DAY29 - [React] useContext 實作篇

  • 分享至 

  • xImage
  •  

今日文章目錄

  • 需求說明
  • 過程紀錄
  • 問題統整
  • 重點筆記
  • 參考資料

需求說明

  • 加入深淺主題色切換。

過程紀錄

  1. 深淺主題色顏色配置。

    export const theme = {
      light: {
        backgroundColor: '#eae0d0',
        color: '#000',
      },
      dark: {
        backgroundColor: '#001529',
        color: '#fff',
      },
    };
    
  2. 建立新資料夾,用來存放 Context 資料。(你可以擁有多個 Context)

  3. 使用React.createContext()方法來建立Context,並設定預設值。

    const ThemeContext = React.createContext(theme.light);
    
    export default ThemeContext;
    
  4. 使用 Context名稱.Provider 來規範 資料分享範圍。
    (Provider 會寫入一個 value props)

    /* 
        我希望所有component都用到,所以我在 App.js 使用:
        ThemeContext.Provider 包住所有子component
    */
    
    function App() {
      return (
        <ThemeContext.Provider value={theme.light}>
          <Router>
            <Switch>
              {routes.map((item) => (
                <Route
                  key={item.path}
                  exact={item.exact}
                  path={item.path}
                >
                  {item.component}
                </Route>
              ))}
            </Switch>
          </Router>
        </ThemeContext.Provider>
      );
    }
    
  5. 子層 component 要取得 Context 資料:

     /* 引入useContext方法 與 目標Context */
     import { useContext } from 'react';
     import ThemeContext from '../context';
    
     function CategoryList(props) {
       /* 變數存取 useContext(目標Context) */
       const theme = useContext(ThemeContext);
    
       return(
         <div style={{backgroundColor: theme.backgroundColor, color: theme.color}}>
          ...
         </div>
       )
     }
    

    來解釋一下:const theme = useContext(ThemeContext);

    • useContext(ThemeContext) : 接收React.createContext()回傳的目標Context物件。
    ThemeContext: React.Context<{ backgroundColor: string; color: string;}>
    
    • 變數 存取 useContext()方法回傳的Context值。
    theme: { backgroundColor: string; color: string;}
    

    這裡的變數theme收到的值,來自 Context.Provider value props,也就是 theme.light
    這裡的變數theme收到的值,來自 Context.Provider value props(theme.light)

目前顯示效果:有成功套入theme.light的指定色。
theme.light淺色效果

再來做深淺色主題切換

既然資料統一放在Context,修改資料的方法也統一放在Context吧!

  1. Context.Provider 的所在元件位置,加入 useState(),並把 {state, setState} 傳下去。

    function App() {
      const [themsState, setThemeState] = useState(theme.light);
    
      return (
        <ThemeContext.Provider value={{ themsState, setThemeState }}>
         ...
        </ThemeContext.Provider>
      );
    }
    
  2. 子層 component 要取得 Context 資料:

        const {state, setState} = useContext(ThemeContext);
    

    點擊切換鈕改值:這裡使用 Antd - Switch

        <Switch
          style={{ marginTop: 10 }}
          checkedChildren="深色主題"
          unCheckedChildren="淺色主題"
          defaultChecked
          onChange={() => {
            setThemeState((pre) => (pre === theme.light ? theme.dark : theme.light));
          }}
        />
    

實現效果:
實現效果

問題統整

Q : 子層既然可以透過useContext()拿到值,那為什麼還要<Context.Provider> ?

A : useContext(MyContext) 只能讓你讀取 context 及訂閱其變更。你仍然需要在 tree 的上層使用<MyContext.Provider> 來提供 context 的值。


Q : Context initialValue 與 Context.Provider的value props 的 差異?

A : 預設值被使用的時機是當開發者沒有使用 <Context.Provider> 卻又使用了 useContext 去取值時會用到;一旦使用 Context.Provider 後,就會以 <Context.Provider value={} /> 中 value 帶入的值為主。

直接來驗證:
Context initialValue 為 theme.light,Context.Provider value props 為 theme.dark的情況
驗證


Q : 既然 Context 與 Context.Provider 可以有多組 且Context.Provider的value props會優先於Context initialValue,那要如何辨認對應的資料是拿哪一個Context.Provider?

A : 離該component最接近的 Context.Provider value props


重點筆記

All consumers that are descendants of a Provider will re-render whenever the Provider’s value prop changes.

這裡也說明了不要濫用 useContext的原因,當 Context更新,所有範圍內的子層都會 re-render,如果各種資料都往 Context 塞,將會造成效能問題。

Only use it for low-frequency updates like the theme and authentication. This is because whenever the context’s value changes, the descendant components of the Provider will be re-rendered.

Context uses reference identity to determine when to re-render.

useContext — This hook takes in a context object and returns whatever is passed in as a value prop in MyContext.Provider .


參考資料


上一篇
DAY28 - [React] useContext 概念篇
下一篇
DAY30 - [React] useMemo 與 後續
系列文
30天CSS、JS、React打造專案零組件30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言