使用 Redux 风格选择器防止 React Context 重新渲染
发布: (2025年12月7日 GMT+8 22:48)
5 min read
原文: Dev.to
Source: Dev.to
问题
在使用 React Context 时,只要 任意 一个上下文值发生变化,所有消费该上下文的组件都会重新渲染。
import { createContext, useState } from 'react';
const AppContext = createContext();
function AppProvider({ children }) {
const [user, setUser] = useState(null);
const [theme, setTheme] = useState('light');
const [notifications, setNotifications] = useState([]);
return (
{children}
);
}
import { useContext } from 'react';
function UserProfile() {
const { user } = useContext(AppContext);
return Hello, {user?.name}!;
}
问题: 当 theme 或 notifications 改变时,UserProfile 也会重新渲染,尽管它只关心 user。
解决方案
use-context-hook 让你只订阅需要的值,使用类似 Redux 的选择器。
import { createContextHook, useContextHook } from 'use-context-hook';
import { useState } from 'react';
// 创建上下文
const AppContext = createContextHook();
// Provider 保持不变
function AppProvider({ children }) {
const [user, setUser] = useState(null);
const [theme, setTheme] = useState('light');
const [notifications, setNotifications] = useState([]);
return (
{children}
);
}
// 组件仅在 `user` 改变时重新渲染
function UserProfile() {
const user = useContextHook(AppContext, 'user');
return Hello, {user?.name}!;
}
现在 UserProfile 只会在 user 变化时更新。
四种灵活的选择器模式
1. 字符串选择器(单值)
const user = useContextHook(AppContext, 'user');
2. 数组选择器(多值)
const { user, theme } = useContextHook(AppContext, ['user', 'theme']);
3. 对象选择器(显式挑选)
const { user, theme } = useContextHook(AppContext, {
user: 1,
theme: 1,
});
4. 函数选择器(Redux 风格)
// 挑选特定字段
const { user, theme } = useContextHook(AppContext, (state) => ({
user: state.user,
theme: state.theme,
}));
// 或者派生出一个值
const userName = useContextHook(AppContext, (state) => state.user?.name);
实际性能影响
- 之前: 每次用户交互会产生 50+ 次不必要的重新渲染
- 之后: 只会有 2‑3 个真正需要更新的组件
示例:在表单中输入时,重新渲染次数从 47 降到 3。
工作原理(技术细节)
useContextHook根据你提供的选择器为组件注册监听器。- 当上下文值变化时,库会在 旧值 与 新值 上分别运行选择器。
- 对选择器的返回结果进行深度比较。
- 仅当选中的值发生变化时才触发重新渲染。
即使是深层嵌套的对象,只要未改变也不会导致重新渲染。
TypeScript 支持
interface AppContextType {
user: User | null;
theme: 'light' | 'dark';
notifications: Notification[];
}
const AppContext = createContextHook();
// 推断为 User | null
const user = useContextHook(AppContext, 'user');
// 推断为 { user: User | null; theme: 'light' | 'dark' }
const { user, theme } = useContextHook(AppContext, ['user', 'theme']);
开箱即用的完整类型推断与安全性。
何时使用?
使用场景
- 上下文对象庞大且包含许多值。
- 组件只需要上下文的特定部分。
- 默认 Context API 导致性能问题。
- 想要 Redux 式的选择器模式,却不想引入 Redux。
不适用的情况
- 上下文仅包含 1‑2 个值。
- 所有组件都需要完整的上下文。
- 已经在使用 Redux、Zustand 等状态管理库并提供了选择性订阅。
安装
npm install use-context-hook
# or
yarn add use-context-hook
# or
pnpm add use-context-hook
在线示例
- 字符串选择器:
- 数组选择器:
- 对象选择器:
- Redux 风格函数选择器:
与其他方案的对比
| 库 | 打包体积 | 选择器模式 | TypeScript | 防止重新渲染 |
|---|---|---|---|---|
| use-context-hook | ~2 KB | 4 种模式 | 完整 | 是 |
| use-context-selector | ~3 KB | 仅函数 | 部分 | 是 |
| React Context(内置) | 0 KB | 无 | 内置 | 否 |
| Redux | ~15 KB+ | 支持 | 支持 | 是(但体积大) |
小结
React Context 功能强大,但默认行为在大型应用中会导致不必要的重新渲染。use-context-hook 提供:
- 体积小 – ~2 KB(压缩后)
- 性能高 – 仅对选中的值做深度比较
- 类型安全 – 完整的 TypeScript 支持
- 灵活 – 四种选择器模式
- 零依赖 – 仅依赖 React 作为 peer dependency
使用它即可获得 Redux 式的选择性订阅,而无需额外的复杂性。
链接
- npm 包:
- GitHub 仓库:
- 完整文档: