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 结构
相关推荐
祝余呀30 分钟前
HTML初学者第四天
前端·html
EutoCool1 小时前
Qt窗口:菜单栏
开发语言·c++·嵌入式硬件·qt·前端框架
浮桥2 小时前
vue3实现pdf文件预览 - vue-pdf-embed
前端·vue.js·pdf
七夜zippoe2 小时前
前端开发中的难题及解决方案
前端·问题
Hockor3 小时前
用 Kimi K2 写前端是一种什么体验?还支持 Claude Code 接入?
前端
杨进军3 小时前
React 实现 useMemo
前端·react.js·前端框架
海底火旺3 小时前
浏览器渲染全过程解析
前端·javascript·浏览器
你听得到113 小时前
揭秘Flutter图片编辑器核心技术:从状态驱动架构到高保真图像处理
android·前端·flutter
驴肉板烧凤梨牛肉堡3 小时前
浏览器是否支持webp图像的判断
前端
Xi-Xu3 小时前
隆重介绍 Xget for Chrome:您的终极下载加速器
前端·网络·chrome·经验分享·github