React Hook的用法: Contex + Reducer(二)

React Hook

簡述

通過上篇文章我們知道使用 State Hook 和 Effect Hook 可以讓函數組件也能夠具有自己的狀態和在組件的各個階段提供鉤子暴露給開發者使用(點擊我查看 State 和 Effect Hook)

實際在業務開發過程中我們往往會結合Redux來實現組件的傳值和全局轉態管理,這裏就來分享一下使用Hook來實現組件的傳值以及狀態管理。

Context Hook

Context Hook可以讓我們實現誇層級去傳遞組件的參數,從而避免了參數從根組件一層一層傳遞下去(爺爺->父親->兒子)的現象,這樣代碼更加扁平,傳遞路徑更加明瞭,也更加容易維護和擴展。
看下面這個設計的Demo,根組件共享給子組件兩個變量,子組件通過Context Hook來獲取根組件傳遞過來的值,同時在這個子組件中又引入了另一個子組件,而這個孫子輩的子組件也可使用Context Hook來直接獲取根組件傳遞過來的值。

基礎關鍵的代碼:
需要在根組件中創建一個共享的組件 StoreContext, 並需要初始化一個值,如下:

export const StoreContext = createContext({});

將需要共享給子組件的數據放在上述所創建的上下文中並作爲根組件去包裹所需要共享的子組件。如下:

<StoreContext.Provider value={StoreObj}>
    {/* 在下面這個子組件中去獲取該父組件共享出去的值 */}
   <ChildContext />
</StoreContext.Provider>

在這樣包裹之後,凡是在 StoreContext.Provider 組件內部的子組件(不管是直接子組件還是間接子組件即孫子組件)都可以通過 useContext 這個鉤子直接使用根組件傳遞過來的值
在對應的子組件中先從React中引入useContext鉤子,並從根組件中引入暴露出來的 StoreContext;就可直接在子組件中獲取根組件的共享數據。
關鍵代碼如下:

import  { useContext } from 'react';
// 獲取根組件傳遞過來的數據
const shareObj = useContext(StoreContext)

效果如下:
在這裏插入圖片描述
在線Demo

Reducer Hook

上述部分解決了組件之間層層傳遞數據的問題,在實際開發應用中我們還需要實現應用狀態的統一管理,比如使用Redux來實現應用狀態的統一管理。現在瞭解下如果使用Hook的話該如何實現。
比如現在一個場景,一個父組件A包含兩個子組件a1,a2(a1,a2是兄弟關係)。其中a1,a2都使用Context Hook來獲取A組件的數據,這時候a1組件觸發了某種操作修改了根組件的數據源,那這個時候a2組件也要同步更新數據。
上述場景在業務開發中經常遇見,比如我們在一個子組件中控制當前頁面的語言顯示,那麼切換語言之後,應用內所有的其他組件也應當更新當前的語言。
這個例子設計的是在根組件中提供了子組件所用的語言包,在按鈕組件中可以切換當前展示的語言,按鈕組件切換語言之後另一個展示文案的組件的語言也需要同步更新。
關鍵點代碼如下(zhLang, enLang表當前語言包的對象):

export const LangContext = createContext(zhLang);
export const ZH_LANG = "ZH_LANG";
export const EN_LANG = "EN_LANG";

第一行是創建一個上下文,共享當前的語言包,默認是中文,第二行第三行實際上是action type 用於區分當前的語言是那種。

const reducer = (state, action) => {
    switch (action.type) {
        case ZH_LANG:
            return zhLang;
        case EN_LANG:
            return enLang
        default:
            return zhLang
    }
}

創建一個reducer 用於去處理不同的action type 業務邏輯,在這裏就是根據子組件發出的action type 來決定當前返回那種語言包。其中第一個參數 state其實是這個應用中的上一個狀態,而action就是子組件dispatch方法中傳遞的參數,一般在action中有一個type用於標記業務處理,如果還需要傳遞額外的參數的話一般添加在action.payload之中。

const [lang, dispatch] = useReducer(reducer, zhLang);

在根組件中將處理業務的reducer和默認的初始值(中文包)傳入useReducer中,這個函數放回的值使用數組的結構賦值得到當前的語言包和dispatch,其中lang就是當前需要共享給子組件的數據,同樣的dispatch方法也要共享到子組件中,這樣子組件可以按需引用並dispatch某一個特定的 action 當然也可以附加額外的一些業務參數。

 <LangContext.Provider value={{lang, dispatch}}>
	<div className="reducer-container">
       <div className="des">{lang.rootTxt}</div>
       <ChangeLantBtns />
       <ShowLangComponent />
	 </div>
 </LangContext.Provider>

更改語言的子組件關鍵代碼:

import { LangContext, ZH_LANG, EN_LANG } from './../ReducerHooks'

先要在子組件中引入 LangContext 結合 useContext 鉤子就可以使用根組件傳遞過來的參數, ZH_LANG, EN_LANG這兩個的引入就好比Redux 中的action type引入一樣,用於業務判斷之後dispatch出去的動作

const { lang, dispatch } = useContext(LangContext);
<Button type="primary" onClick={()=>{dispatch({type:EN_LANG,payload:"other Params"})}}>{lang.en}</Button>

第一行是使用解構賦值的方式得到 LangContext.Provider 組件傳遞過來的lang, dispatch。這樣子組件就可以使用當前的語言包並展示數據(${lang.en})
當我們點擊按鈕的時候,就會觸發 dispatch方法,該方法接受的參數其實是會傳遞給到事先註冊的reducer方法之中的action。

對於第二個子組件只是要使用父組件傳遞過來的數據的話,那這個套路就和上一部分使用useContext一樣。
關鍵點代碼:

import { LangContext } from './../ReducerHooks;

引入根組件創建的上下文變量

const { lang } = useContext(LangContext) ;

useContext 的固定寫法,也是老套路,這樣就獲取了父組件傳遞過來的共享語言包。

<div>{lang.txt1}</div>

獲取lang之後,就可以直接使用父組件共享出的當前語言包中的語言描述了。
這樣就實現了之前的功能,在這裏父組件共享語言包給子組件,其中按鈕組組件可以切換語言,切換語言之後 ShowLangComponent 組件中的語言也會變化。整個過程是使用 useContext hook來實現父組件數據的共享和 uesReducer hook 管理應用的狀態(這裏的狀態就是共享給子組件的語言包是英文狀態還是中文狀態)
整體效果如下:
在這裏插入圖片描述
在線Demo

總結

  1. 使用 uesContext 解決了組件層層傳值的問題
  2. 使用uesReducer 解決了應用狀態的統一管理
  3. uesContext + uesReducer 可以實現Redux的效果而且沒有Redux那麼重,整個過程清晰明瞭,數據流向清晰,易擴展易維護
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章