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































