文章

React Context API 介紹

React Context API 介紹

React Context API 是一個強大的工具,用來在組件樹中共享狀態,而不必逐層傳遞 props。它允許我們在父組件和任意深度的子組件之間共享數據,這對於全局狀態管理(如用戶信息、主題、語言設置等)非常有用。

Context API 的主要用途:

  • 避免 props drilling:在 React 中,當父組件需要將狀態傳遞給深層嵌套的子組件時,通常需要逐層傳遞 props。Context API 可以讓你直接在子組件中獲取父組件的狀態,而不需要經過中間層。
  • 全局狀態共享:可以在應用的任何地方使用 Context,無需手動傳遞數據。

1. Context API 的基本結構

Context API 主要包括三個核心概念:

  1. React.createContext():用來創建一個 Context 對象。
  2. <Context.Provider>:用來提供數據,通過這個組件來傳遞數據給子組件。
  3. useContext()<Context.Consumer>:用來在子組件中獲取上下文數據。

2. Context API 的使用步驟

(1) 創建 Context

首先,我們使用 React.createContext() 創建一個 Context,這將返回一個包含 ProviderConsumer 的對象。

1
2
3
4
import React from 'react';

// 創建一個 Context
const MyContext = React.createContext();

(2) 提供數據(Provider)

在父組件中使用 Provider 包裹需要傳遞數據的組件樹,並將共享的數據傳遞給 Providervalue 屬性。

1
2
3
4
5
6
7
8
9
function App() {
  const user = { name: 'John', age: 30 };

  return (
    <MyContext.Provider value={user}>
      <ChildComponent />
    </MyContext.Provider>
  );
}

在這個範例中,我們將 user 對象作為 value 傳遞給 MyContext.Provider,這樣 ChildComponent 及其子組件就能夠訪問這個上下文數據。

(3) 使用數據(useContext 或 Consumer)

在子組件中,我們可以通過 useContext Hook 或 Context.Consumer 獲取共享數據。

使用 useContext Hook:

1
2
3
4
5
6
7
8
9
10
11
12
import React, { useContext } from 'react';

function ChildComponent() {
  const user = useContext(MyContext); // 使用 useContext 獲取數據

  return (
    <div>
      <p>名字: {user.name}</p>
      <p>年齡: {user.age}</p>
    </div>
  );
}

使用 Context.Consumer

1
2
3
4
5
6
7
8
9
10
11
12
function ChildComponent() {
  return (
    <MyContext.Consumer>
      {user => (
        <div>
          <p>名字: {user.name}</p>
          <p>年齡: {user.age}</p>
        </div>
      )}
    </MyContext.Consumer>
  );
}

useContext 是使用 Context 的更簡潔方法,而 Consumer 提供了更多控制,但語法較為冗長。


3. Context 的實際應用場景

(1) 主題切換

Context 非常適合處理全局的 UI 主題狀態,如深色模式和淺色模式切換。

範例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// themeContext.js
import React, { useState, createContext, useContext } from 'react';

const ThemeContext = createContext();

export function ThemeProvider({ children }) {
  const [theme, setTheme] = useState('light');

  const toggleTheme = () => {
    setTheme(prevTheme => (prevTheme === 'light' ? 'dark' : 'light'));
  };

  return (
    <ThemeContext.Provider value=Jekyll::Drops::ThemeDrop>
      {children}
    </ThemeContext.Provider>
  );
}

export function useTheme() {
  return useContext(ThemeContext);
}

在這裡,我們創建了一個 ThemeContext 並通過 ThemeProvider 將主題狀態和切換主題的邏輯傳遞給子組件。然後,任何組件都可以使用 useTheme 來訪問當前主題和切換主題的功能。

使用 Context 的組件:

1
2
3
4
5
6
7
8
9
10
11
12
import React from 'react';
import { useTheme } from './themeContext';

function ThemeToggleButton() {
  const { theme, toggleTheme } = useTheme();

  return (
    <button onClick={toggleTheme}>
      切換到 {theme === 'light' ? '暗黑模式' : '淺色模式'}
    </button>
  );
}

(2) 用戶認證狀態

Context 也非常適合處理用戶的認證狀態,這樣我們可以在整個應用中共享用戶信息。

範例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// authContext.js
import React, { useState, createContext, useContext } from 'react';

const AuthContext = createContext();

export function AuthProvider({ children }) {
  const [user, setUser] = useState(null);

  const login = (userData) => {
    setUser(userData);
  };

  const logout = () => {
    setUser(null);
  };

  return (
    <AuthContext.Provider value=>
      {children}
    </AuthContext.Provider>
  );
}

export function useAuth() {
  return useContext(AuthContext);
}

在這個範例中,我們創建了一個 AuthContext 來管理用戶的登入狀態,並在應用中提供 loginlogout 功能。

使用 Context 的組件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import React from 'react';
import { useAuth } from './authContext';

function UserProfile() {
  const { user, logout } = useAuth();

  if (!user) {
    return <p>請先登入</p>;
  }

  return (
    <div>
      <p>用戶名: {user.name}</p>
      <button onClick={logout}>登出</button>
    </div>
  );
}

4. 多個 Context 的組合使用

有時我們可能需要在應用中使用多個 Context,例如主題和認證狀態。我們可以通過多層 Provider 包裹來實現:

1
2
3
4
5
6
7
8
9
10
function App() {
  return (
    <AuthProvider>
      <ThemeProvider>
        <UserProfile />
        <ThemeToggleButton />
      </ThemeProvider>
    </AuthProvider>
  );
}

這樣,UserProfileThemeToggleButton 組件既能訪問認證狀態,也能訪問主題狀態。


5. Context 的注意事項

  • 性能問題:頻繁變化的 Context 值會導致每次重新渲染時所有使用該 Context 的子組件都重新渲染。對於高頻變化的數據,可以考慮將其移到更局部的狀態中。
  • 避免過度使用:雖然 Context 是非常強大的工具,但在小規模數據共享時(如兩三層的 props 傳遞),過度使用 Context 可能會使代碼複雜化。

總結

React Context API 提供了一個有效的方式來處理組件之間的數據共享,尤其是當需要跨多層組件傳遞數據時,能夠避免繁瑣的 props 傳遞。通過正確使用 Context,我們可以輕鬆管理全局狀態,如用戶身份驗證、主題設置等,使應用更加靈活和可維護。

本文章以 CC BY 4.0 授權