使用 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}!;
}

问题:themenotifications 改变时,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 KB4 种模式完整
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 仓库:
  • 完整文档:
Back to Blog

相关文章

阅读更多 »

Reatom:随你成长的状态管理

碎片化问题 现代前端开发有一个常见的模式: - 从简单的 useState hook 开始 - 需要共享状态?添加 Context - Context re‑...