React?Hooks useReducer?逃避deps組件渲染次數增加陷阱
前言
在快樂使用 React Hooks
開發(fā)自定義 Hooks
過程中,使用了 useEffect
,useReducer
,useRef
,useCallback
等官方提供的 Hooks
,將一些通用邏輯抽離出來,提高代碼復用性。
但在組合使用 useEffect
,useReducer
,React.memo
時,發(fā)生了組件在狀態(tài)未發(fā)生變化時觸發(fā)渲染,因為此動作發(fā)生在 mousemove
鼠標移動時,所以組件不必要渲染次數非常多。
自定義 Hooks 簡單實現
import { useReducer } from "react"; const reducer = (state, action) => { const { sliding, lastPos, ratio } = state; switch (action.type) { case "start": return { ...state, slideRange: action.slideRange, lastPos: action.x, sliding: true, }; case "move": if (!sliding) { return state; } const offsetX = action.x - lastPos; const newRatio = ratio + offsetX / state.slideRange; if (newRatio > 1 || newRatio < 0) { return state; } return { ...state, lastPos: action.x, ratio: newRatio, }; case "end": if (!sliding) { return state; } return { ...state, sliding: false, }; case "updateRatio": return { ...state, ratio: action.ratio, }; default: return state; } }; export function useSlider(initialState) { const [state, dispatch] = useReducer(reducer, initialState); return [state, dispatch]; }
在組件中使用自定義 Hooks
const [state, dispatch] = useSlider(initialState); const { ratio, sliding, lastPos, slideRange } = state; useEffect(() => { const onSliding = (e) => { dispatch({ type: "move", x: e.pageX }); }; const onSlideEnd = () => { dispatch({ type: "end" }); }; document.addEventListener("mousemove", onSliding); document.addEventListener("mouseup", onSlideEnd); return () => { document.removeEventListener("mousemove", onSliding); document.removeEventListener("mouseup", onSlideEnd); }; }, [dispatch]); const handleThumbMouseDown = useCallback( (event) => { const hotArea = hotAreaRef.current; dispatch({ type: "start", x: event.pageX, slideRange: hotArea.clientWidth, }); if (event.target.className !== "point") { dispatch({ type: "updateRatio", ratio: (event.pageX - 30) / hotArea.clientWidth, }); } }, [dispatch] );
鼠標每次移動,都會觸發(fā) dispatch({ type: "move", x: e.pageX })
,在 reducer
函數中,當 !sliding
時,不修改 state 數據原樣返回,但是組件仍然進行了渲染。
提前阻止 dispatch 觸發(fā)
將 sliding
判斷移動到 useEffect
中,提前阻止 dispatch
觸發(fā),并將 sliding
設置到 useEffect(fn, deps)
deps 中,保證監(jiān)聽函數中能取到 sliding
最新值。
useEffect(() => { const onSliding = (e) => { if(!sliding) { return; } dispatch({ type: "move", x: e.pageX }); }; const onSlideEnd = () => { if (!sliding) { return; } dispatch({ type: "end" }); }; document.addEventListener("mousemove", onSliding); document.addEventListener("mouseup", onSlideEnd); return () => { document.removeEventListener("mousemove", onSliding); document.removeEventListener("mouseup", onSlideEnd); }; }, [sliding]);
優(yōu)化后再測試
鼠標僅移動時,sliding
為 false
,直接 return
,不會觸發(fā) dispatch
動作。
好處
避免了組件在 state
未修改時不必要渲染。
壞處
部分處理邏輯被移動到使用自定義 hooks
的組件中,sliding
數據改變時,add EventListener
函數會重新注冊。
結論
不能為了不在 useEffect(fn, deps)
設置 deps
,使用 useReducer
,并把所有數據變更都放在 reducer
中。 本篇文章通過把不修改 reducer
state
的動作提前阻止,避免使用此自定義 hooks 的組件發(fā)生不必要渲染,提高代碼復用性的同時也兼顧了組件性能。
題外
React.memo
對props
進行淺比較,一種組件性能優(yōu)化方式useCallback(fn, deps)
緩存函數useMemo(() => fn, deps)
緩存昂貴變量- useState(initialState)惰性初始state
,
initialState` 只會在組件初始渲染中起作用,后續(xù)渲染時會被
參考文獻
以上就是React Hooks 之 useReducer 逃避deps后增加組件渲染次數的陷阱的詳細內容,更多關于React Hooks useReducer組件渲染的資料請關注腳本之家其它相關文章!
相關文章
D3.js(v3)+react 實現帶坐標與比例尺的柱形圖 (V3版本)
這篇文章主要介紹了D3.js(v3)+react 制作 一個帶坐標與比例尺的柱形圖 (V3版本) ,本文通過實例代碼文字相結合的形式給大家介紹的非常詳細,需要的朋友可以參考下2019-05-05