面試官:React-Redux 它是怎么更新界面的?
react-redux 的核心是 訂閱 store 變化 并 觸發(fā)組件重新渲染。它利用 React 的 context 和 useSyncExternalStore 來高效地管理狀態(tài)和 UI 更新。下面詳細講解 react-redux 是如何更新界面的。
react-redux 更新 UI 的流程
- **組件連接 Redux store**(Provider 共享全局狀態(tài))
- 組件訂閱 store 變化(useSelector / connect 監(jiān)聽數(shù)據(jù)變化)
- **狀態(tài)改變時,觸發(fā) store.subscribe**(Redux dispatch 觸發(fā) store 更新)
- 對比新舊狀態(tài),決定是否重新渲染(避免不必要的 UI 更新)
- 通知組件重新渲染(React useState 或 forceUpdate 觸發(fā)渲染)
1. Redux store 如何連接到 React
在 react-redux 中,我們通過 Provider 讓整個應(yīng)用訪問 store:
import { Provider } from "react-redux";
import { store } from "./store";
export default function App() {
return (
<Provider store={store}>
<MyComponent />
</Provider>
);
}
- Provider 使用 React Context 傳遞 store。
- 子組件可以用 useSelector 訪問 Redux 狀態(tài)。
2. 組件如何訂閱 Redux 狀態(tài)
組件可以使用 useSelector 訂閱 store 里的狀態(tài):
import { useSelector } from "react-redux";
function MyComponent() {
const count = useSelector(state => state.counter.value);
return <p>Count: {count}</p>;
}
useSelector 如何監(jiān)聽狀態(tài)變化?
- useSelector 內(nèi)部會調(diào)用 store.subscribe() 訂閱 Redux store 變化
- 當 dispatch 修改 store 時,所有 useSelector 訂閱的組件都會執(zhí)行
- useSelector 會對比新舊狀態(tài)(默認用 === 淺比較)
- 如果狀態(tài)沒變,組件不會重新渲染,避免不必要的更新
3. Redux dispatch 如何觸發(fā) UI 更新
組件通過 dispatch 觸發(fā) Redux store 更新:
import { useDispatch } from "react-redux";
import { increment } from "./counterSlice";
function MyComponent() {
const dispatch = useDispatch();
return <button onClick={() => dispatch(increment())}>+1</button>;
}
dispatch 更新流程
- dispatch(action) 觸發(fā) Redux store 更新
- Redux reducer 計算新 state
- store 觸發(fā) store.subscribe() 通知所有 useSelector 訂閱的組件
- useSelector 比較狀態(tài),如果變化則觸發(fā)組件 重新渲染
4. react-redux 內(nèi)部是如何訂閱 store 的?
useSelector 的底層實現(xiàn)
在 react-redux 中,useSelector 用 useSyncExternalStore 監(jiān)聽 store:
import { useSyncExternalStore } from "react";
function useSelector(selector) {
const store = useContext(StoreContext);
return useSyncExternalStore(
store.subscribe, // 訂閱 Redux store
() => selector(store.getState()) // 獲取最新狀態(tài)
);
}
useSyncExternalStore 如何工作?
- **訂閱
store
**(store.subscribe
) - 檢測狀態(tài)是否變化(通過
store.getState()
獲取最新值) - 如果狀態(tài)變了,觸發(fā)組件重新渲染(React 重新執(zhí)行組件)
Redux 更新 UI 的完整流程
- dispatch(action) 觸發(fā) store 更新
- reducer 計算新 state
- store 調(diào)用 store.subscribe() 通知組件
- 組件的 useSelector 重新執(zhí)行,并對比狀態(tài)
- 如果狀態(tài)變化,則 觸發(fā) React 重新渲染
react-redux UI 更新的優(yōu)化
1. 避免不必要的渲染
- useSelector 只會讓組件更新受影響的狀態(tài),而不是整個 store。
- 默認使用 === 淺比較,確保狀態(tài)真的變化才會觸發(fā)渲染:
const value = useSelector(state => state.value, (a, b) => a === b);
- 如果 useSelector 依賴對象,可以使用 reselect 進行 Memoization。
2. 使用 useCallback 和 useMemo
- useDispatch() 生成的 dispatch 函數(shù)不會變,但 useSelector 可能導致組件重新渲染:
const data = useMemo(() => expensiveCalculation(state), [state]);
const handleClick = useCallback(() => dispatch(increment()), [dispatch]);
3. 代碼分片(Lazy Load)
- 使用 redux-toolkit 的 lazyReducerEnhancer 進行動態(tài)加載 reducer,減少初始化開銷。
總結(jié)
步驟 | React Redux UI 更新流程 |
1. 組件連接 store | Provider 通過 Context 提供 Redux store |
2. 組件訂閱狀態(tài) | useSelector 監(jiān)聽 store 變化 |
3. 狀態(tài)變更 | dispatch 觸發(fā) store 更新 |
4. 組件重新渲染 | useSyncExternalStore 檢測狀態(tài)變更,觸發(fā) UI 更新 |
5. 性能優(yōu)化 | useSelector 只更新受影響的組件,減少不必要渲染 |
React-Redux 通過 useSelector 監(jiān)聽 store,dispatch 觸發(fā) store 變更,useSyncExternalStore 檢測 state 變化,決定是否重新渲染組件,從而實現(xiàn)高效的 UI 更新。