React Context用法总结

1. 基本概念

1.1 什么是 Context

Context 提供了一种在组件树中共享数据的方式,而不必通过 props 显式地逐层传递。它主要用于共享那些对于组件树中许多组件来说是"全局"的数据。

1.2 基本用法

jsx 复制代码
// 1. 创建 Context
const ThemeContext = React.createContext('light');

// 2. 提供 Context
function App() {
  return (
    <ThemeContext.Provider value="dark">
      <Toolbar />
    </ThemeContext.Provider>
  );
}

// 3. 消费 Context
function Toolbar() {
  return (
    <div>
      <ThemedButton />
    </div>
  );
}

function ThemedButton() {
  const theme = React.useContext(ThemeContext);
  return <button className={theme}>Themed Button</button>;
}

2. Context API 详解

2.1 创建 Context

jsx 复制代码
// 创建 Context 并设置默认值
const UserContext = React.createContext({
  name: 'Guest',
  isAdmin: false
});

// 导出 Context 供其他组件使用
export default UserContext;

2.2 Provider 组件

jsx 复制代码
function App() {
  const [user, setUser] = useState({
    name: 'John',
    isAdmin: true
  });

  return (
    <UserContext.Provider value={user}>
      <div className="app">
        <MainContent />
        <Sidebar />
      </div>
    </UserContext.Provider>
  );
}

2.3 消费 Context

使用 useContext Hook(推荐)
jsx 复制代码
function UserProfile() {
  const user = React.useContext(UserContext);
  
  return (
    <div>
      <h2>User Profile</h2>
      <p>Name: {user.name}</p>
      <p>Role: {user.isAdmin ? 'Admin' : 'User'}</p>
    </div>
  );
}
使用 Consumer 组件
jsx 复制代码
function UserRole() {
  return (
    <UserContext.Consumer>
      {user => (
        <span>
          Role: {user.isAdmin ? 'Admin' : 'User'}
        </span>
      )}
    </UserContext.Consumer>
  );
}

3. 高级用法

3.1 多个 Context 组合

jsx 复制代码
const ThemeContext = React.createContext('light');
const UserContext = React.createContext({ name: 'Guest' });

function App() {
  return (
    <ThemeContext.Provider value="dark">
      <UserContext.Provider value={{ name: 'John' }}>
        <Layout />
      </UserContext.Provider>
    </ThemeContext.Provider>
  );
}

function Layout() {
  const theme = React.useContext(ThemeContext);
  const user = React.useContext(UserContext);

  return (
    <div className={theme}>
      <header>Welcome, {user.name}</header>
      <main>Content</main>
    </div>
  );
}

3.2 动态 Context

jsx 复制代码
const ThemeContext = React.createContext({
  theme: 'light',
  toggleTheme: () => {}
});

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

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

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

function ThemeToggleButton() {
  const { theme, toggleTheme } = React.useContext(ThemeContext);
  return (
    <button onClick={toggleTheme}>
      Current theme: {theme}
    </button>
  );
}

3.3 Context 与 TypeScript

typescript 复制代码
// 定义 Context 类型
interface UserContextType {
  name: string;
  isAdmin: boolean;
  updateUser: (name: string) => void;
}

const UserContext = React.createContext<UserContextType | undefined>(undefined);

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

// Provider 组件
function UserProvider({ children }: { children: React.ReactNode }) {
  const [name, setName] = useState('Guest');
  const [isAdmin] = useState(false);

  const updateUser = (newName: string) => {
    setName(newName);
  };

  return (
    <UserContext.Provider value={{ name, isAdmin, updateUser }}>
      {children}
    </UserContext.Provider>
  );
}

4. 最佳实践

4.1 创建自定义 Hook

jsx 复制代码
// 创建自定义 Hook 封装 Context 逻辑
function useTheme() {
  const context = React.useContext(ThemeContext);
  if (context === undefined) {
    throw new Error('useTheme must be used within a ThemeProvider');
  }
  return context;
}

// 使用自定义 Hook
function ThemedButton() {
  const { theme, toggleTheme } = useTheme();
  return (
    <button onClick={toggleTheme} className={theme}>
      Toggle Theme
    </button>
  );
}

4.2 分离 Context 逻辑

jsx 复制代码
// contexts/theme.js
export const ThemeContext = React.createContext({
  theme: 'light',
  toggleTheme: () => {}
});

export function ThemeProvider({ children }) {
  const [theme, setTheme] = useState('light');
  
  const value = {
    theme,
    toggleTheme: () => setTheme(t => t === 'light' ? 'dark' : 'light')
  };

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

export function useTheme() {
  const context = React.useContext(ThemeContext);
  if (context === undefined) {
    throw new Error('useTheme must be used within a ThemeProvider');
  }
  return context;
}

4.3 性能优化

jsx 复制代码
// 使用 memo 避免不必要的重渲染
const UserInfo = React.memo(function UserInfo() {
  const { name } = useUser();
  return <div>User: {name}</div>;
});

// 分离状态,避免不相关的更新
function AppProvider({ children }) {
  return (
    <ThemeProvider>
      <UserProvider>
        <SettingsProvider>
          {children}
        </SettingsProvider>
      </UserProvider>
    </ThemeProvider>
  );
}

5. 常见问题和解决方案

5.1 避免重渲染

jsx 复制代码
// 使用 useMemo 优化 Context value
function ThemeProvider({ children }) {
  const [theme, setTheme] = useState('light');

  const value = useMemo(() => ({
    theme,
    toggleTheme: () => setTheme(t => t === 'light' ? 'dark' : 'light')
  }), [theme]);

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

5.2 处理嵌套 Context

jsx 复制代码
// 创建组合 Provider
function AppProviders({ children }) {
  return (
    <AuthProvider>
      <ThemeProvider>
        <UserProvider>
          {children}
        </UserProvider>
      </ThemeProvider>
    </AuthProvider>
  );
}

// 使用组合 Provider
function App() {
  return (
    <AppProviders>
      <MainApp />
    </AppProviders>
  );
}

6. 总结

6.1 使用场景

  1. 主题切换
  2. 用户认证状态
  3. 语言偏好
  4. 全局配置
  5. 路由状态共享

6.2 最佳实践建议

  1. 适度使用 Context
  2. 创建专门的 Provider 组件
  3. 使用自定义 Hook 封装 Context 逻辑
  4. 注意性能优化
  5. 合理组织 Context 结构
相关推荐
anOnion4 小时前
构建无障碍组件之Carousel Pattern
前端·html·交互设计
ssshooter4 小时前
Tauri 2 iOS 开发避坑指南:文件保存、Dialog 和 Documents 目录的那些坑
前端·后端·ios
Можно5 小时前
深入理解 ES6 Proxy:与 Object.defineProperty 的全面对比
前端·javascript·vue.js
Birdy_x5 小时前
接口自动化项目实战(1):requests请求封装
开发语言·前端·python
天天向上10246 小时前
vue el-table实现拖拽排序
前端·javascript·vue.js
柳杉7 小时前
Three.js × Blender:从建模到 Web 3D 的完整工作流深度解析
前端·javascript·数据可视化
reembarkation8 小时前
vue3中使用howler播放音频列表
前端·vue.js·音视频
手握风云-8 小时前
基于 Java 的网页聊天室(三)
服务器·前端·数据库
weixin199701080169 小时前
《识货商品详情页前端性能优化实战》
前端·性能优化
Forever7_9 小时前
重磅!Vue3 手势工具正式发布!免费使用!
前端·前端框架·前端工程化