React 中的 Context(上下文)是什么,它解决了什么问题,以及如何正确使用它

12 阅读2分钟

1. Context 是什么?

Context 提供了一种在组件树中共享数据的方式,无需通过 props 手动逐层传递。它主要用于共享被视为"全局"的数据。

2. Context 解决的问题

解决了 "prop drilling"(属性钻取)问题,即:避免将 props 通过多层组件传递。

3. 基本使用方法

3.1 创建 Context
// ThemeContext.js
import { createContext } from 'react';

export const ThemeContext = createContext({
  theme: 'light',
  toggleTheme: () => {},
});
3.2 提供 Context
// App.js
import { ThemeContext } from './ThemeContext';
import { useState } from 'react';

function App() {
  const [theme, setTheme] = useState('light');
  
  const toggleTheme = () => {
    setTheme(prevTheme => prevTheme === 'light' ? 'dark' : 'light');
  };

  return (
    <ThemeContext.Provider value={{ theme, toggleTheme }}>
      <MainContent />
    </ThemeContext.Provider>
  );
}
3.3 消费 Context
// 方式1:使用 useContext Hook
import { useContext } from 'react';
import { ThemeContext } from './ThemeContext';

function ThemedButton() {
  const { theme, toggleTheme } = useContext(ThemeContext);
  
  return (
    <button 
      style={{ background: theme === 'light' ? '#fff' : '#000' }}
      onClick={toggleTheme}
    >
      Current theme: {theme}
    </button>
  );
}

// 方式2:使用 Consumer
function ThemedText() {
  return (
    <ThemeContext.Consumer>
      {({ theme }) => (
        <p style={{ color: theme === 'light' ? '#000' : '#fff' }}>
          This text uses the current theme
        </p>
      )}
    </ThemeContext.Consumer>
  );
}

4. 高级用法

4.1 组合多个 Context
function App() {
  return (
    <ThemeContext.Provider value={themeValue}>
      <UserContext.Provider value={userValue}>
        <MainContent />
      </UserContext.Provider>
    </ThemeContext.Provider>
  );
}
4.2 创建自定义 Hook
// useTheme.js
import { useContext } from 'react';
import { ThemeContext } from './ThemeContext';

export function useTheme() {
  const context = useContext(ThemeContext);
  if (context === undefined) {
    throw new Error('useTheme must be used within a ThemeProvider');
  }
  return context;
}
4.3 动态 Context
function DynamicThemeProvider({ children }) {
  const [theme, setTheme] = useState('light');
  const [themeConfig, setThemeConfig] = useState({
    light: {
      background: '#fff',
      color: '#000'
    },
    dark: {
      background: '#000',
      color: '#fff'
    }
  });

  const value = {
    theme,
    themeConfig: themeConfig[theme],
    setTheme,
    setThemeConfig
  };

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

5. 完整案例

5.1 函数组件
// 1. 创建 Context
import React, { createContext, useContext, useState } from 'react';

const UserContext = createContext();

// 2. 创建 Provider 组件
export function UserProvider({ children }) {
  const [user, setUser] = useState({
    name: "John",
    theme: "dark"
  });

  return (
    <UserContext.Provider value={{ user, setUser }}>
      {children}
    </UserContext.Provider>
  );
}

// 3. 创建自定义 Hook 来使用 Context
export function useUser() {
  const context = useContext(UserContext);
  if (context === undefined) {
    throw new Error('useUser must be used within a UserProvider');
  }
  return context;
}

// 4. 在组件中使用
function DeepNestedComponent() {
  const { user, setUser } = useUser();
  
  return (
    <div>
      <h1>Hello, {user.name}!</h1>
      <button onClick={() => setUser({ ...user, theme: user.theme === 'dark' ? 'light' : 'dark' })}>
        Toggle Theme
      </button>
    </div>
  );
}

// 5. 在应用中包裹 Provider
function App() {
  return (
    <UserProvider>
      <DeepNestedComponent />
    </UserProvider>
  );
}

6. 最佳实践

  1. 适当的使用场景

    • 全局主题配置
    • 用户认证信息
    • 语言偏好
    • Redux/Mobx 等状态管理的替代方案(小型应用)
  2. 避免过度使用

    • Context 的变更会导致所有消费该 Context 的组件重新渲染
    • 不要用 Context 来传递频繁变化的值
  3. 性能优化

function OptimizedProvider({ children }) {
  const [state, setState] = useState({
    theme: 'light',
    user: null
  });

  // 将方法用 useCallback 包裹,避免不必要的重渲染
  const setTheme = useCallback((theme) => {
    setState(prev => ({ ...prev, theme }));
  }, []);

  const value = useMemo(() => ({
    ...state,
    setTheme
  }), [state, setTheme]);

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

7. 常见陷阱和注意事项

  1. 默认值陷阱
// 错误示例
const MyContext = createContext();

// 正确示例
const MyContext = createContext(null);
// 或者提供一个默认值
const MyContext = createContext({ theme: 'light' });
  1. Provider 值变化
// 错误示例 - 每次渲染都会创建新对象
function BadProvider({ children }) {
  return (
    <ThemeContext.Provider value={{ theme: 'light' }}>
      {children}
    </ThemeContext.Provider>
  );
}

// 正确示例 - 使用 state 或 useMemo
function GoodProvider({ children }) {
  const value = useMemo(() => ({
    theme: 'light'
  }), []);

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

Context 是 React 中非常强大的特性,但需要谨慎使用。在适当的场景下使用 Context 可以大大简化代码结构,提高开发效率。但过度使用可能会导致组件复用性降低和性能问题。