面試官:UseSelector 返回的一個State數據很大,這個要怎么優化
如果 useSelector 返回的 state 數據很大,可能會導致 不必要的組件重新渲染,影響性能。優化方式主要有以下幾種:
1. 精確選擇數據,避免返回整個對象
? 錯誤做法(返回整個 state 對象)
const bigData = useSelector(state => state.bigData);
?? 問題:
- 任何 state.bigData 內部的字段變化都會觸發組件重新渲染,即使組件只用到其中一部分數據。
? 正確做法(只選擇需要的字段)
const someValue = useSelector(state => state.bigData.someValue);
? 優勢:
- 組件只會在 someValue 變化時重新渲染,而不會因 bigData 的其他字段變化而重新渲染。
2. 使用 reselect 進行 Memoization
如果計算 state 需要進行復雜的計算(如 filter、map 等),可以使用 reselect 緩存計算結果。
?? 安裝 reselect
npm install reselect
?? 創建 selector
import { createSelector } from "reselect";
// 原始數據選擇器
const selectBigData = state => state.bigData;
// 計算派生數據
export const selectFilteredData = createSelector(
[selectBigData],
bigData => bigData.filter(item => item.active) // 只返回 active 狀態的數據
);
?? 組件中使用 selector
import { useSelector } from "react-redux";
import { selectFilteredData } from "./selectors";
const MyComponent = () => {
const filteredData = useSelector(selectFilteredData);
return <div>{filteredData.length}</div>;
};
? 優勢:
- createSelector 只有在 state.bigData 變化時才會 重新計算,否則返回緩存結果。
- 避免不必要的計算,提升性能。
3. useSelector 第二個參數 equalityFn 自定義比較
默認情況下,useSelector 使用 淺比較(===) 來判斷狀態是否變化。如果 state 是 深層對象,可以使用 自定義比較函數 進行優化。
?? 默認 useSelector(淺比較,可能會導致不必要的渲染)
const data = useSelector(state => state.bigData); // 任何字段變化都會重新渲染
?? 使用 shallowEqual 進行淺層對比
import { shallowEqual, useSelector } from "react-redux";
const data = useSelector(state => state.bigData, shallowEqual);
? 優勢:
- shallowEqual 僅在 bigData 的 頂層字段 發生變化時才觸發組件重新渲染。
?? 自定義 equalityFn(僅在 id 變化時更新)
const selectedItem = useSelector(
state => state.bigData.find(item => item.id === 1),
(prev, next) => prev.id === next.id // 只在 id 變化時重新渲染
);
? 優勢:
- 避免 bigData 其他字段變化時,導致不必要的重新渲染。
4. 拆分 state,減少 store 更新影響范圍
如果 bigData 很大,可以考慮拆分 store 結構,讓 Redux 多個 slice 管理不同部分的數據。
?? 優化前(單個 slice 存放大量數據)
const rootReducer = combineReducers({
bigData: bigDataReducer
});
?? 優化后(拆分成多個 slice)
const rootReducer = combineReducers({
users: usersReducer,
products: productsReducer
});
?? 組件按需獲取 state
const users = useSelector(state => state.users);
const products = useSelector(state => state.products);
? 優勢:
- 避免 bigData 變化時,所有依賴 bigData 的組件都重新渲染。
5. 組件拆分,減少渲染范圍
如果 useSelector 獲取的數據很大,考慮 拆分組件,讓子組件只訂閱需要的數據。
?? 錯誤示例(整個組件訂閱大數據)
const ParentComponent = () => {
const bigData = useSelector(state => state.bigData);
return (
<div>
{bigData.map(item => (
<p key={item.id}>{item.name}</p>
))}
</div>
);
};
?? 優化示例(拆分成子組件,每個子組件獨立訂閱狀態)
const ListItem = ({ id }) => {
const item = useSelector(state => state.bigData.find(i => i.id === id));
return <p>{item.name}</p>;
};
const ParentComponent = () => {
const itemIds = useSelector(state => state.bigData.map(i => i.id));
return (
<div>
{itemIds.map(id => (
<ListItem key={id} id={id} />
))}
</div>
);
};
? 優勢:
- 只有受影響的 ListItem 組件會重新渲染,而不是整個 ParentComponent。
6. 結合 useMemo 進行優化
如果 useSelector 返回的數據需要復雜計算,可以用 useMemo 緩存結果,避免重復計算。
?? 錯誤示例(計算邏輯直接在 useSelector 內部)
const filteredData = useSelector(state =>
state.bigData.filter(item => item.active) // 每次都會重新計算
);
?? 優化示例(用 useMemo 緩存計算結果)
const data = useSelector(state => state.bigData);
const filteredData = useMemo(() => data.filter(item => item.active), [data]);
? 優勢:
- useMemo 只有在 data 變化時才會重新計算,提高性能。
?? 總結
優化方法 | 思路 | 適用場景 |
1. 精確選擇數據 |
只返回 需要的字段,而不是整個 | 適用于 |
2. 使用 |
緩存計算結果,避免重復計算 | 適用于 依賴計算、列表過濾 場景 |
3. 自定義 | 只在 關鍵數據變化 時觸發渲染 | 適用于 深層數據結構 |
4. 拆分 | 將 | 適用于 大規模應用 |
5. 組件拆分 | 讓子組件獨立訂閱 | 適用于 列表渲染、大數據量場景 |
6. | 避免 | 適用于 復雜計算 |
?? 最終優化思路:
- 盡量讓 useSelector 只選擇最小數據。
- 使用 reselect 避免不必要的計算。
- 盡量拆分組件,減少不必要的渲染。
這樣可以讓 react-redux 更高效地更新 UI,提升應用性能! ??