JavaScript >> Javascript 文檔 >  >> React

如何以及何時在 React with Hooks 中使用上下文

前段時間,我寫了一篇關於在 React 中使用 Context API 的文章。但是,我在該頁面上的大多數示例都使用了類組件,static contextType , 和 Consumer ,這是處理 Context 的傳統方式,在 TYOOL 2021 中,我們需要漂亮、乾淨、功能強大的組件。很長一段時間後,我最近需要使用 Context 來做一些事情,我想要一個只使用現代語法的更簡潔的解釋。我決定在這裡寫一些後續文章,以實際使用 Context。

Context 允許你在任意數量的 React 組件之間傳遞數據,無論嵌套如何。

Redux 還是上下文?

在一個非常小的應用程序中,您可能只使用 Context 來滿足您的大部分全局數據存儲需求,但在大型生產環境中,您可能會使用 Redux 進行全局狀態管理。 Redux 仍然提供改進的性能、改進的調試能力、架構一致性、使用中間件的能力等等。因此,Context 不能替代適當的全局狀態管理系統。

通常,Context 的示例會顯示類似暗模式切換的內容,這對於一個快速示例來說是很好的。但是,在小型博客或網站之外使用深色主題的真實示例可能會涉及用戶的設置,他們可以保存並在任何會話中持續存在,而不僅僅是 localStorage 中的臨時狀態 通過上下文切換。在這種情況下,你的暗模式狀態將被保存到 Redux 中,因為它可能會保存為當前登錄的整個 user 對象,並且需要 API 調用來進行更改。

所以我將提供一個關於如何使用現代 React 語法設置 Context 的摘要,然後進入一個使用 Context 的示例以及它是如何工作的。

總結

如果你只是想複製一些代碼來創建、提供和使用上下文,這裡是:

您通常會有一個使用 createContext 的文件 並導出一個 Provider 包裝:

創建
import React, { createContext } from 'react'

export const Context = createContext()

export const Provider = ({ children }) => {
  const [state, setState] = useState({})

  const value = {
    state,
    setState,
  }

  return <Context.Provider value={value}>{children}</Context.Provider>
}

然後,您將使用 Provider 包裝任何需要訪問上下文狀態的組件 :

提供
import React from 'react'

import { Provider } from './Context'
import { ConsumingComponent } from './ConsumingComponent'

export const Page = () => {
  return (
    <div>
      <Provider>
        <ConsumingComponent />
      </Provider>
    </div>
  )
}

消費組件現在可以使用 useContext 鉤子訪問數據:

消耗
import React, { useContext } from 'react'

import { Context } from './Context'

export const ConsumingComponent = () => {
  const { state } = useContext(Context)

  return null
}

示例

那麼什麼時候應該使用 Context,如果它沒有用於與 Redux 相同的目的呢?嗯,根據我的經驗,Context 對於更本地化和可重用的東西是有意義的。例如,您有一個儀表板小部件,該小部件具有多種類型的小部件通用的控件。假設每個小部件都接收數據,但可以在條形圖、折線圖或表格視圖之間更改視圖。在這種情況下,您可以創建一個 Context Provider 來設置控件的狀態並更新它們,並將它們傳遞給任何消費者。

你使用 createContext() 創建一個上下文,它也創建一個 Provider 和一個 Consumer ,但您只需要 Provider ,這將允許樹中它下面的任何 React 元素使用 Context。

創建上下文

DashboardWidget.context.js
import React, { useState, createContext } from 'react'

export const DashboardWidgetContext = createContext()

export const DashboardWidgetProvider = ({ children }) => {
  const [dataView, setDataView] = useState('table')

  const handleChangeView = value => {
    setDataViewView(value)
  }

  const value = {
    dataView,
    handleChangeView,
  }

  return <DashboardWidgetContext.Provider value={value}>{children}</DashboardWidgetContext.Provider>
}

消費上下文

然後你可能有一個處理動作的組件。這是一個人為的例子,但它會包含一個 select 可讓您在條形圖、折線圖或表格視圖之間切換。也許它還有一個“導出為 CSV”按鈕,或者其他一些可以應用於小部件中所有數據的操作。現在您不必單獨處理每個小部件的控件,而是一次性處理所有小部件。

在這裡你可以看到 useContext hook 允許您從 Context 訪問數據。

DashboardWidgetControls.js
import React, { useContext } from 'react'

import { DashboardWidgetContext } from './DashboardWidget.context'

export const DashboardWidgetControls = ({ label }) => {
  const { dataView, handleChangeView } = useContext(DashboardWidgetContext)

  return (
    <div>
      <select value={dataView} onChange={handleChangeView}>
        <option value="bar_graph">Bar Graph</option>
        <option value="line_chart">Line Chart</option>
        <option value="table">Table</option>
      </select>
    </div>
  )
}

無論您需要在本地級別上做什麼獨特的數據,您都可以在單個組件中執行,同時仍然可以訪問外部控制數據。這部分可能會單獨處理,因為它可能是分組或堆疊條形圖,或嵌套表,並且可能需要在該級別上進行很多調整。

SomeDataComponent.js
import React, { useContext } from 'react'

import { DashboardWidgetContext } from './DashboardWidget.context'

export const SomeDataComponent = () => {
  const { dataView } = useContext(DashboardWidgetContext)

  switch (dataView) {
    case 'table':
      return <Table />
    case 'line_chart':
      return <LineChart />
    case 'bar_chart':
      return <BarChart />
  }
}

提供上下文

現在,無論您在哪裡需要該小部件,都可以引入 Provider 和控件。我將它放入一個包裝器組件中:

import React from 'react'

import { DashboardWidgetProvider } from './DashboardWidget.context'
import { DashboardWidgetControls } from './WidgetControls'

export const DashboardWidget = ({ title, children }) => {
  return (
    <WidgetProvider>
      <section>
        <h2>{title}</h2>
        <WidgetControls />
        {children}
      </section>
    </WidgetProvider>
  )
}
DashboardPage.js
import React from 'react';

import { DashboardWidget } from './DashboardWidget';

export const DashboardPage = () => {
  return (
    <div>
      <h1>Dashboard</h1>

      <DashboardWidget title="Distance of Planets to the Sun">
        <PlanetDistance />
      </DashboardWidgetProvider>

      <DashboardWidget title="Time Dilation and the Speed of Light">
        <SpeedOfLight />
      </DashboardWidget>
    </div>
  );
};

也許在這種情況下,實際數據存儲在 Redux 中,因為它可能會在除此儀表板組件之外的其他地方使用,並且只需要在本地化級別上處理控件。這是我可以看到 Context 很有意義的一個示例,因為手動傳遞該數據可能會開始變得不直觀,或者會出現大量重複來處理相同類型的狀態。我覺得嘗試在 Redux 中處理這樣的事情會很麻煩,因為如果您希望多個小部件一次都可見,您需要它看起來像 widgets: { widget1: 'bar', widget2: 'table' } 或者為每個單獨的小部件設置一個單獨的商店。

結論

我希望這是一個相對清晰的例子,說明您可能會使用 Context 以及使用它的現代語法。


Tutorial JavaScript 教程
  1. 14 個 jQuery 模態對話框

  2. 使用 npx 運行要點

  3. Monorepo 使用 Lerna、常規提交和 Github 包

  4. 撲熱息痛.js💊| #95:解釋這段 JavaScript 代碼

  5. 在 React Native 中構建多項選擇測驗

  6. Promise.allSettled() 和 Promise.any() 有什麼問題❓

  7. 使用 Supabase 快速輕鬆地進行身份驗證

  1. 分析 Javascript 方法的性能

  2. 如何在 Ionic/Capacitor 應用中實現 Google OAuth2

  3. 編寫自己的 React Hooks - TDD 示例

  4. 有效的學習和反饋循環

  5. toFixed() 和 toPrecision() 的區別?

  6. 編寫你的第一個 React UI 庫 - 第 2 部分:故事書

  7. 在 JavaScript 中結束

  1. 5分鐘生成reactjs的離線文檔

  2. 在 CSS-in-JS 中你可以做的 5 件事你不知道

  3. 動態組件,Vue.js 的超能力

  4. Node.js 應用程序的持續部署