React

2023.10.23

用 React Context API 實作跨組件傳值的功能


需求

現在如果使用 Vue 開發網站,我會習慣用 Vite + Pinia 幫我實現狀態管理與跨組件傳值的功能。

至於 React 也有蠻多選擇,較知名的是 Redux,以下是幾個常見的 Library:

  • Redux
    Redux 是一個非常流行的狀態管理 Library,用於管理應用的全域狀態。你可以將應用的狀態儲存在一個全域儲存中,並通過 connect 高階元件來連接 React 元件。Redux 較適用於大型應用。
  • Mobx
    Mobx 是另一個流行的狀態管理庫,它使用可觀察對象和響應式機制來管理狀態。它可以更簡單地實現狀態管理,適用於中小型應用。
  • React Context API
    React 內建的 Context API 允許你建立全域的上下文,以便跨元件傳遞資料。雖然它不如 Redux 或 Mobx 強大,但對於小型應用和簡單的跨元件資料傳遞來說非常足夠。
  • Zustand
    Zustand 是一個小巧的狀態管理庫,專門為 React 設計。它使用 Hooks 和 Context API,讓你能夠方便地管理狀態,適用於中小型應用。
  • Recoil
    Recoil 是由 Facebook 開發的狀態管理庫,它專為 React 設計。提供了一種簡單的方式來管理元件之間的狀態,並且支援原子狀態管理。
  • Jotai
    Jotai 是另一個小型的狀態管理庫,使用了 React Hooks 和 Context。它的設計非常簡單,易於理解和使用。(Jotai 是「狀態」的拼音,非常粗暴好理解 😂)

因為我的需求非常簡單,只是要跨組件傳遞會員資料,不需要用到狀態管理,因此我選擇的方法是「React Context API」,不需要額外安裝套件,直接使用 React API 即可。

實作的功能與流程為:使用者切換路由時,都會先打一隻 /me 的 api 取得即時的會員資料與權限,我再用這些資料,顯示會員基本資料以及做權限控管。

使用 Context API 建立資料

首先,我們要把「打 /me API」這件事情,放在一個獨立的 context 檔案中

▼ 先新增檔案:src/context/UserContext.js,再用 crateContext 以及 useContext 建立並儲存資料

import React, { createContext, useContext, useState, useEffect } from 'react'
import { getUserApi } from '../api/auths'

const UserContext = createContext()

export const useUser = () => {
  return useContext(UserContext)
}

export const UserProvider = ({ children }) => {
  const [loading, setLoading] = useState(true)
  const [user, setUser] = useState({})

  useEffect(() => {
    async function fetchUser() {
      try {
        const res = await getUserApi()
        setUser(res.data)
        setLoading(false)
      } catch (error) {
        console.error('Response error:', error)
      }
    }
    fetchUser()
  }, [])

  return (
    <UserContext.Provider value={user}>
      {/* 使用 loading 來判斷是否已經取得使用者資料,如果還沒取得,則不顯示 children */}
      {loading ? null : children}
    </UserContext.Provider>
  )
}

使用 Provider 共享 User 資料

▼ 打開 App.jsx,匯入 UserProvider

import { UserProvider } from './context/UserContext'

const App = () => {
  return (
    // 使用 UserProvider,跨組件傳遞 user 資料
    <UserProvider>
      <AppContent />
    </UserProvider>
  )
}

在 App.jsx 讀取 User 資料

▼ 我們可以用剛剛的 useUser 讀到 User 資料,寫法如下

import { UserProvider, useUser } from './context/UserContext'

const App = () => {
  const user = useUser()
  return (
    <UserProvider>
      // 這邊處理取得的 user 資料
    </UserProvider>
  )
}

但這樣寫 user 會是 undefined,原因是我們在 <UserProvider> 設定之前,就讀取了 useUser,因此 user 為 undefined

▼ 所以比較好的寫法,是在 <UserProvider> 內部使用 useUser Hook,改寫如下

import { UserProvider, useUser } from './context/UserContext'

const App = () => {
  return (
    <UserProvider>
      <AppContent />
    </UserProvider>
  )
}

const AppContent = () => {
  const user = useUser()
  return (
    // 處理並渲染 user 資料
  )
}

在其他頁面讀取 User 資料

假設我們有一頁 Home.jsx 也要讀取使用者資料,只要使用 useUser 就能輕鬆取得

import { useUser } from './context/UserContext'

const Home = () => {
  const user = useUser()
  return (
    // 處理並渲染 user 資料
  )
}

寫法就跟前面一樣,方便易懂好上手。

如果只是想要單純傳值,不需要複雜的狀態管理,可以考慮使用內建的 Context API,簡單解決煩惱!

歡迎給我點鼓勵,讓我知道你來過 :)

1
Subscribe
Notify of
guest

0 則留言
Inline Feedbacks
View all comments
Copyright (C) MUKI space* / Reborn Theme All Rights Reserved.