v19.2Latest

使用 Reducer 與 Context 擴展規模

Reducer 讓你可以整合元件的狀態更新邏輯。Context 讓你可以將資訊深入傳遞給其他元件。你可以將 reducer 與 context 結合起來,以管理複雜畫面的狀態。

您將學習
  • 如何將 reducer 與 context 結合
  • 如何避免透過 props 傳遞狀態與 dispatch
  • 如何將 context 與狀態邏輯保存在單獨的檔案中

將 reducer 與 context 結合

在這個來自 reducer 簡介的範例中,狀態由一個 reducer 管理。reducer 函數包含了所有的狀態更新邏輯,並在此檔案底部宣告:

Reducer 有助於保持事件處理函式簡潔。然而,隨著應用程式增長,您可能會遇到另一個困難。目前,tasks 狀態和 dispatch 函式僅在最上層的 TaskApp元件中可用。若要讓其他元件讀取任務清單或進行修改,您必須明確地向下傳遞當前狀態以及修改它的事件處理函式作為 props。

例如,TaskApp將任務清單和事件處理函式傳遞給TaskList

TaskList會將事件處理函式傳遞給Task

在像這樣的小範例中,這種方式運作良好,但如果你在中間有數十個或數百個元件,向下傳遞所有狀態和函式可能會相當令人沮喪!

這就是為什麼,作為透過屬性傳遞的替代方案,你可能會想把 tasks 狀態和 dispatch函式都放入上下文。這樣一來,樹狀結構中位於TaskApp下方的任何元件都可以讀取任務並分派動作,而無需重複的「屬性鑽取」。

以下是如何將 reducer 與上下文結合:

  1. 建立上下文。
  2. 狀態和 dispatch 函數放入上下文。
  3. 樹狀結構中的任何地方使用上下文。

步驟 1:建立上下文

useReducerHook 會回傳當前的tasks以及讓你更新它們的dispatch 函數:

要將它們傳遞到樹狀結構下方,你需要建立兩個獨立的上下文:

  • TasksContext 提供當前的任務清單。
  • TasksDispatchContext提供讓元件可以 dispatch 動作的函數。

將它們從一個獨立的檔案匯出,以便之後可以從其他檔案匯入:

這裡,你將 null 作為預設值傳遞給兩個上下文。實際的值將由 TaskApp元件提供。

步驟 2:將狀態和 dispatch 放入上下文

現在你可以在你的 TaskApp元件中匯入這兩個上下文。取得由useReducer()回傳的tasksdispatch,並將它們提供給下方的整個樹狀結構:

目前,你同時透過 props 和上下文傳遞資訊:

在下一步中,您將移除 props 的傳遞。

步驟 3:在樹狀結構中的任何地方使用 context

現在您不需要將任務列表或事件處理函式沿著樹狀結構向下傳遞:

相反地,任何需要任務列表的元件都可以從 TasksContext中讀取它:

要更新任務列表,任何元件都可以從 context 中讀取dispatch 函式並呼叫它:

現在TaskApp元件不會向下傳遞任何事件處理函式,而TaskList也不會將任何事件處理函式傳遞給Task 元件。每個元件都讀取它所需的 context:

狀態仍然「存在」於頂層的 TaskApp 元件中,由 useReducer管理。 但其 tasksdispatch現在可以透過匯入和使用這些上下文,供樹狀結構下方的每個元件使用。

將所有連線移至單一檔案

您不必這樣做,但您可以透過將 reducer 和上下文都移至單一檔案來進一步簡化元件。目前,TasksContext.js 僅包含兩個上下文宣告:

這個檔案即將變得擁擠!您將把 reducer 移至同一個檔案。然後您將在同一個檔案中宣告一個新的TasksProvider 元件。這個元件將把所有部分整合在一起:

  1. 它將使用 reducer 管理狀態。
  2. 它將為下方的元件提供兩個上下文。
  3. 它將 將 children 作為 prop 接收,以便您可以傳遞 JSX 給它。

這從您的 TaskApp元件中移除了所有的複雜性和連線:

您也可以從 使用 上下文的 TasksContext.js匯出函數:

當元件需要讀取上下文時,可以透過這些函數來進行:

這並不會改變任何行為,但它讓您之後可以進一步拆分這些上下文或在這些函數中加入一些邏輯。現在所有上下文和 reducer 的連接邏輯都在TasksContext.js中。這使得元件保持乾淨整潔,專注於它們顯示的內容,而不是它們從哪裡獲取資料:

您可以將 TasksProvider 視為知道如何處理任務的螢幕部分,將 useTasks視為讀取它們的方式,將useTasksDispatch視為從樹中下方任何元件更新它們的方式。

注意

useTasksuseTasksDispatch這樣的函數被稱為自訂 Hook。 如果您的函數名稱以 use開頭,它就被視為自訂 Hook。這讓您可以在其中使用其他 Hook,例如useContext

隨著您的應用程式成長,您可能會有很多像這樣的上下文-reducer 配對。這是一種強大的方式來擴展您的應用程式,並且在您想要存取樹中深層的資料時,無需太多工作即可提升狀態

總結

  • 您可以將 reducer 與 context 結合,讓任何元件讀取並更新其上層的狀態。
  • 要向下層元件提供狀態與 dispatch 函數:
    1. 建立兩個 context(一個用於狀態,一個用於 dispatch 函數)。
    2. 從使用 reducer 的元件提供這兩個 context。
    3. 從需要讀取它們的元件中使用任一 context。
  • 您可以將所有佈線邏輯移至單一檔案,進一步簡化元件。
    • 您可以匯出一個元件,例如TasksProvider來提供 context。
    • 您也可以匯出自訂 Hook,例如useTasksuseTasksDispatch來讀取它。
  • 您的應用程式中可以擁有多個這樣的 context-reducer 配對。