【react】状态管理Context

发布于:2025-03-04 ⋅ 阅读:(15) ⋅ 点赞:(0)

目录

一、Context 的核心概念

1. 什么是 Context?

2. 核心三要素:

二、使用场景分析

适合场景 ✅

不适合场景 ❌

三、完整使用示例

1. 创建 Context

2. 提供 Context

3. 消费 Context

四、context使用详解

1.Context.Consumer 使用详解

1. 基础用法(类组件)

2. 嵌套 Consumer(多 Context)

2、useContext 使用详解

1. 基础用法(函数组件)

2. 复杂对象消费

3、对比总结

4、高级模式示例

1. 自定义 Hook 封装

2. 类组件中使用 Context(两种方式)

5、常见错误示例

1. 未提供 Provider

2. 直接传递对象导致重复渲染

五、性能优化策略

六、常见问题解决方案

1. 默认值不生效?

2. 组件不更新?

3. 多个 Context 如何管理?

4. Class 组件如何使用?

七、Context vs Redux

八、最佳实践


一、Context 的核心概念

1. 什么是 Context?

  • React 官方提供的跨层级组件通信方案

  • 解决 props drilling(深层级 props 传递)问题

  • 适合全局共享数据(如主题、用户身份、语言等)

2. 核心三要素:

  1. React.createContext()
    创建 Context 对象,可指定默认值

    const ThemeContext = React.createContext('light');
  2. Context.Provider
    数据提供者组件,通过 value 属性传递数据

    <ThemeContext.Provider value="dark">
      <App />
    </ThemeContext.Provider>
  3. Context.Consumer 或 useContext
    数据消费者,用于获取上下文值

    // 类组件
    <ThemeContext.Consumer>
      {value => <div>{value}</div>}
    </ThemeContext.Consumer>
    
    // 函数组件
    const theme = useContext(ThemeContext);

二、使用场景分析

适合场景 ✅
  1. 全局主题切换

  2. 用户认证信息共享

  3. 多语言国际化

  4. 全局配置参数

  5. 跨 3+ 层组件的数据传递

不适合场景 ❌
  1. 高频更新数据(优先考虑状态管理库如 Redux)

  2. 简单父子组件通信(直接用 props)

  3. 组件内部私有状态(使用 useState)

三、完整使用示例

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

const ThemeContext = createContext({
  theme: 'light',
  toggleTheme: () => {},
});

export default ThemeContext;
2. 提供 Context
// App.jsx
import { useState } from 'react';
import ThemeContext from './ThemeContext';
import Toolbar from './Toolbar';

export default function App() {
  const [theme, setTheme] = useState('light');

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

  return (
    <ThemeContext.Provider value={{ theme, toggleTheme }}>
      <Toolbar />
    </ThemeContext.Provider>
  );
}
3. 消费 Context
// Toolbar.jsx
import { useContext } from 'react';
import ThemeContext from './ThemeContext';

export default function Toolbar() {
  const { theme, toggleTheme } = useContext(ThemeContext);

  return (
    <div style={{ background: theme === 'dark' ? '#333' : '#fff' }}>
      <button onClick={toggleTheme}>
        Toggle Theme ({theme})
      </button>
    </div>
  );
}

四、context使用详解

1.Context.Consumer 使用详解

1. 基础用法(类组件)
import React from 'react';

// 创建 Context
const ThemeContext = React.createContext('light');

// 类组件中使用 Consumer
class ThemedButton extends React.Component {
  render() {
    return (
      <ThemeContext.Consumer>
        {(theme) => (
          <button style={{ 
            background: theme === 'dark' ? '#333' : '#fff',
            color: theme === 'dark' ? '#fff' : '#333'
          }}>
            {this.props.children}
          </button>
        )}
      </ThemeContext.Consumer>
    );
  }
}

// Provider 设置
function App() {
  return (
    <ThemeContext.Provider value="dark">
      <ThemedButton>Click me</ThemedButton>
    </ThemeContext.Provider>
  );
}
2. 嵌套 Consumer(多 Context)
const ThemeContext = React.createContext('light');
const UserContext = React.createContext('Guest');

function Toolbar() {
  return (
    <ThemeContext.Consumer>
      {(theme) => (
        <UserContext.Consumer>
          {(user) => (
            <div style={{ 
              background: theme, 
              padding: '10px' 
            }}>
              Logged in as: {user}
            </div>
          )}
        </UserContext.Consumer>
      )}
    </ThemeContext.Consumer>
  );
}

// Provider 嵌套
function App() {
  return (
    <ThemeContext.Provider value="dark">
      <UserContext.Provider value="Alice">
        <Toolbar />
      </UserContext.Provider>
    </ThemeContext.Provider>
  );
}

2、useContext 使用详解

1. 基础用法(函数组件)
import React, { useContext } from 'react';

const ThemeContext = React.createContext('light');

function ThemedButton() {
  // 使用 useContext 获取值
  const theme = useContext(ThemeContext);

  return (
    <button style={{ 
      background: theme === 'dark' ? '#333' : '#fff',
      color: theme === 'dark' ? '#fff' : '#333'
    }}>
      Click me
    </button>
  );
}

// 动态更新示例
function App() {
  const [theme, setTheme] = React.useState('light');

  return (
    <ThemeContext.Provider value={theme}>
      <ThemedButton />
      <button onClick={() => setTheme(t => t === 'light' ? 'dark' : 'light')}>
        Toggle Theme
      </button>
    </ThemeContext.Provider>
  );
}
2. 复杂对象消费
const UserContext = React.createContext({
  name: 'Guest',
  role: 'user',
  login: () => {}
});

function UserPanel() {
  // 解构获取多个值
  const { name, role, login } = useContext(UserContext);

  return (
    <div>
      <p>Name: {name}</p>
      <p>Role: {role}</p>
      <button onClick={login}>Login</button>
    </div>
  );
}

function App() {
  const [user, setUser] = React.useState({
    name: 'Alice',
    role: 'admin'
  });

  const loginHandler = () => {
    setUser({ name: 'Bob', role: 'editor' });
  };

  return (
    <UserContext.Provider value={{
      ...user,
      login: loginHandler
    }}>
      <UserPanel />
    </UserContext.Provider>
  );
}

3、对比总结

特性 Context.Consumer useContext
组件类型 适用于类组件 仅适用于函数组件
代码简洁性 需要嵌套函数 直接返回值,代码更简洁
多 Context 使用 需要多层嵌套 Consumer 可多次调用 useContext
动态更新 自动响应 Provider 更新 自动响应 Provider 更新
性能优化 需注意避免不必要的渲染 结合 memo 使用更易优化

4、高级模式示例

1. 自定义 Hook 封装
// 创建自定义 Hook
function useTheme() {
  const context = useContext(ThemeContext);
  if (!context) {
    throw new Error('useTheme must be used within a ThemeProvider');
  }
  return context;
}

// 使用自定义 Hook
function ThemeToggle() {
  const { theme, toggleTheme } = useTheme();

  return (
    <button onClick={toggleTheme}>
      Current Theme: {theme}
    </button>
  );
}
2. 类组件中使用 Context(两种方式)
// 方式一:static contextType
class ThemeDisplay extends React.Component {
  static contextType = ThemeContext;

  render() {
    return <div>Current Theme: {this.context}</div>;
  }
}

// 方式二:Consumer
class ThemeButton extends React.Component {
  render() {
    return (
      <ThemeContext.Consumer>
        {(theme) => (
          <button style={{ background: theme }}>
            {this.props.children}
          </button>
        )}
      </ThemeContext.Consumer>
    );
  }
}

5、常见错误示例

1. 未提供 Provider
// 错误:使用默认值但期望动态更新
function BrokenComponent() {
  const theme = useContext(ThemeContext); // 默认值 'light'
  return <div>{theme}</div>; // 永远显示 'light'
}

// 正确做法:确保外层有 Provider
function App() {
  return (
    <ThemeContext.Provider value="dark">
      <BrokenComponent />
    </ThemeContext.Provider>
  );
}
2. 直接传递对象导致重复渲染
// 错误写法:每次渲染创建新对象
function App() {
  return (
    <ThemeContext.Provider value={{ theme: 'dark' }}> {/* 每次渲染新对象 */}
      <Child />
    </ThemeContext.Provider>
  );
}

// 正确做法:使用 useMemo
function App() {
  const value = useMemo(() => ({ theme: 'dark' }), []);
  return (
    <ThemeContext.Provider value={value}>
      <Child />
    </ThemeContext.Provider>
  );
}

通过这些示例,可以更清晰地理解 Context.Consumer 和 useContext 的具体用法与差异。实际开发中:

  • 优先使用 useContext(函数组件)

  • 类组件使用 static contextType 或 Consumer

  • 复杂场景结合 useMemo + memo 优化性能

五、性能优化策略

  1. 拆分 Context
    将高频更新和低频更新的数据分离到不同 Context

    // 分开定义
    const SettingsContext = createContext();
    const UserContext = createContext();
  2. 记忆化值对象
    使用 useMemo 避免不必要的重渲染

    const contextValue = useMemo(() => ({ theme, toggleTheme }), [theme]);
  3. 使用选择器模式
    通过高阶组件实现精准订阅(类似 Redux 的 connect)

    const useTheme = () => {
      const { theme } = useContext(ThemeContext);
      return theme;
    };
  4. 避免嵌套 Provider
    保持 Provider 层级扁平化

六、常见问题解决方案

1. 默认值不生效?
  • 确保组件树中没有匹配的 Provider 时才会使用默认值

  • 总是显式提供 Provider 的 value 属性

2. 组件不更新?
  • 检查 Provider 的 value 是否总是创建新对象

  • 使用 useMemo 优化:

    const value = useMemo(() => ({ theme, toggleTheme }), [theme]);
3. 多个 Context 如何管理?
  • 使用组合 Provider:

    <UserProvider>
      <ThemeProvider>
        <App />
      </ThemeProvider>
    </UserProvider>
4. Class 组件如何使用?
  • 使用 static contextType 或 Context.Consumer

    class MyClass extends React.Component {
      static contextType = ThemeContext;
      
      render() {
        const { theme } = this.context;
        return <div>{theme}</div>;
      }
    }

七、Context vs Redux

特性 Context API Redux
学习成本 较高
调试工具 无内置 Redux DevTools
中间件支持 不支持 支持
适用场景 中小应用 大型复杂应用
性能优化 需手动优化 自动优化
代码量 较少 较多

八、最佳实践

  1. 避免滥用:只在需要跨多层组件通信时使用

  2. 类型安全:配合 TypeScript 定义 context 类型

    interface ThemeContextType {
      theme: 'light' | 'dark';
      toggleTheme: () => void;
    }
  3. 文档规范:为每个 Context 添加使用说明

  4. 测试策略:使用 Mock Provider 进行单元测试

码字不易,各位大佬点点赞呗