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 结构
相关推荐
GIS之路14 分钟前
GIS 数据转换:使用 GDAL 将 Shp 转换为 GeoJSON 数据
前端
JIngJaneIL24 分钟前
基于springboot + vue房屋租赁管理系统(源码+数据库+文档)
java·开发语言·前端·数据库·vue.js·spring boot·后端
天天扭码26 分钟前
以浏览器多进程的角度解构页面渲染的整个流程
前端·面试·浏览器
你们瞎搞41 分钟前
Cesium加载20GB航测影像.tif
前端·cesium·gdal·地图切片
南山安1 小时前
Tailwind CSS:顺风CSS
javascript·css·react.js
milanleon1 小时前
使用Spring Security进行登录认证
java·前端·spring
excel1 小时前
🚀 从零开始:如何从 GPTsAPI.net 申请 API Key 并打造自己的 AI 服务
前端
期待のcode2 小时前
@RequestBody的伪表单提交场景
java·前端·vue.js·后端
栀秋6662 小时前
防抖 vs 节流:从百度搜索到京东电商,看前端性能优化的“节奏哲学”
前端·javascript
一颗烂土豆2 小时前
vfit.js v2.0.0 发布:精简、语义化与核心重构 🎉
前端·vue.js·响应式设计