为何使用useMemo()无法阻止React组件额外重渲染?
解决组件重复渲染问题的正确方法
首先要明确:useMemo 并不是用来阻止组件重渲染的工具——它的作用是缓存昂贵计算的结果,避免每次渲染都重复执行计算。组件依然会在依赖的状态或 props 变化时重新渲染,包括你通过 useAppSelector 获取的 Redux 状态变化时。
你看到 console.log 反复输出,大概率是以下几种原因:
- 组件初始渲染时,
blogs是初始状态(比如空数组); useEffect触发异步请求后,Redux 状态更新(比如 loading 状态变化、数据加载完成),导致组件重新渲染;- 可能存在不必要的状态更新,比如请求完成后重复设置相同的
results数组,或者父组件触发了重渲染。
下面是针对性的解决方案:
1. 用 reselect 缓存选择器结果
如果你的选择器每次都返回新的数组引用(比如 Redux 切片中每次更新状态时都创建新的 results 数组),即使数据内容没变化,useAppSelector 也会因为引用不同触发重渲染。这时可以用 Redux Toolkit 内置的 createSelector 来创建缓存的选择器:
import { createSelector } from '@reduxjs/toolkit'; // 先定义基础选择器 const selectIntroBlogsData = (state: RootState) => state.introBlogsSlice.data; // 创建缓存的选择器,只有当 data.results 实际变化时才返回新值 const selectBlogs = createSelector( [selectIntroBlogsData], (data) => data.results ); // 在组件中使用 const blogs = useAppSelector(selectBlogs);
2. 用 React.memo 包裹组件
如果组件的重渲染是因为父组件重渲染导致的(而非自身依赖的状态变化),可以用 React.memo 包裹组件,它会浅比较组件的 props,只有当 props 变化时才触发重渲染:
const BlogsComponent = React.memo(() => { const blogs = useAppSelector(selectBlogs); const dispatch = useAppDispatch(); useEffect(() => { dispatch(fetchSomeBlogs()); }, []); console.log("blogs", blogs); return ( // 组件内容 ); });
3. 检查 Redux 切片中的不必要状态更新
确保你的 reducer 只在数据真正变化时才更新状态。比如在异步请求完成的 case 中,先判断新数据是否和当前状态一致,一致的话直接返回原状态:
import { isEqual } from 'lodash'; // 可以用 lodash 的深比较工具 case fetchSomeBlogs.fulfilled: // 对比新数据和当前数据,一致则不更新 if (isEqual(state.data.results, action.payload.results)) { return state; } return { ...state, data: { ...state.data, results: action.payload.results, loading: false } };
4. 确认 useEffect 只执行一次
检查你的 useEffect 是否意外触发多次。可以在 useEffect 内加日志确认:
useEffect(() => { console.log("Dispatching fetchSomeBlogs"); dispatch(fetchSomeBlogs()); }, []);
如果日志输出多次,说明组件可能被多次挂载/卸载,或者依赖数组有误(但你这里是空数组,应该只执行一次)。
内容的提问来源于stack exchange,提问作者Vilayat Safarov
相关产品推荐
相关产品推荐

