React中useSelector与reselect createSelector的设计目的及使用疑问
解答你的React Redux疑问
一、useSelector存在的意义:连接全局状态和组件响应式
React默认的响应式确实是「状态变化触发组件重渲染」,但这个逻辑只覆盖组件自身state和父组件传递的props。而Redux的store是全局状态池,组件要获取全局状态,总不能手动写一堆繁琐的订阅逻辑:
// 手动订阅store的麻烦写法 import store from './store'; function MyComponent() { const [data, setData] = useState(store.getState().someValue); useEffect(() => { const unsubscribe = store.subscribe(() => { setData(store.getState().someValue); }); return unsubscribe; }, []); // ... }
useSelector就是帮你封装了这堆订阅、监听、更新的逻辑:
- 自动订阅store,当store状态变化时重新执行你的状态选取函数
- 对比选取前后的值,只有变化时才触发组件重渲染
- 只让组件获取它需要的那部分状态,不用把整个store传给组件,更贴合组件职责单一的原则
二、为什么需要createSelector做缓存?
你说的「值只是嵌套数据的引用,值不变组件就不会重渲染」不全对,问题出在useSelector的比较逻辑和状态选取的方式:
1. useSelector的默认比较是严格相等(===)
useSelector默认用===对比两次选取的结果:
- 如果取的是基础类型(比如
state.user.id),只要值不变,组件就不会重渲染 - 但如果选取逻辑是生成新对象/数组(比如过滤、映射),比如:
这时候哪怕todos没有任何改动,只要store里其他状态(比如购物车)更新了,useSelector会重新执行这个过滤函数,返回一个新的数组引用,// 每次执行都会返回新数组,哪怕todos本身没变化 const completedTodos = useSelector(state => state.todos.filter(t => t.completed));===对比不相等,组件就会无意义地重渲染。
2. createSelector的作用:缓存计算结果
createSelector会缓存你的衍生状态计算结果,只有当它依赖的源状态变化时,才重新计算并返回新值;否则直接返回缓存的旧引用。比如上面的例子改成:
import { createSelector } from '@reduxjs/toolkit'; const selectTodos = state => state.todos; const selectCompletedTodos = createSelector( [selectTodos], (todos) => todos.filter(t => t.completed) ); // 在组件里使用 const completedTodos = useSelector(selectCompletedTodos);
这样只有当state.todos真正变化时,才会重新执行过滤函数,返回新数组;其他状态变化时,直接返回缓存的旧数组引用,===对比相等,组件就不会乱重渲染。
3. 为什么直接计算衍生值不好?
不是「从store取值不好」,而是每次都重复计算衍生值不好:
- 浪费计算资源:比如复杂的过滤、排序、聚合逻辑,每次组件更新都重新跑一遍,性能开销大
- 触发不必要的重渲染:组件无意义地重渲染会导致界面卡顿,尤其是列表类组件
总结
- useSelector是帮你把Redux全局状态和React组件的响应式逻辑绑定起来,省去手动写订阅代码的麻烦
- createSelector是解决「衍生状态重复计算+不必要重渲染」的性能问题,让组件只在真正关心的状态变化时才更新
内容的提问来源于stack exchange,提问作者Ser5
相关产品推荐
相关产品推荐

