React?Hooks useReducer?逃避deps組件渲染次數(shù)增加陷阱
前言
在快樂使用 React Hooks
開發(fā)自定義 Hooks
過程中,使用了 useEffect
,useReducer
,useRef
,useCallback
等官方提供的 Hooks
,將一些通用邏輯抽離出來,提高代碼復(fù)用性。
但在組合使用 useEffect
,useReducer
,React.memo
時(shí),發(fā)生了組件在狀態(tài)未發(fā)生變化時(shí)觸發(fā)渲染,因?yàn)榇藙?dòng)作發(fā)生在 mousemove
鼠標(biāo)移動(dòng)時(shí),所以組件不必要渲染次數(shù)非常多。
自定義 Hooks 簡單實(shí)現(xiàn)
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] );
鼠標(biāo)每次移動(dòng),都會(huì)觸發(fā) dispatch({ type: "move", x: e.pageX })
,在 reducer
函數(shù)中,當(dāng) !sliding
時(shí),不修改 state 數(shù)據(jù)原樣返回,但是組件仍然進(jìn)行了渲染。
提前阻止 dispatch 觸發(fā)
將 sliding
判斷移動(dòng)到 useEffect
中,提前阻止 dispatch
觸發(fā),并將 sliding
設(shè)置到 useEffect(fn, deps)
deps 中,保證監(jiān)聽函數(shù)中能取到 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)化后再測試
鼠標(biāo)僅移動(dòng)時(shí),sliding
為 false
,直接 return
,不會(huì)觸發(fā) dispatch
動(dòng)作。
好處
避免了組件在 state
未修改時(shí)不必要渲染。
壞處
部分處理邏輯被移動(dòng)到使用自定義 hooks
的組件中,sliding
數(shù)據(jù)改變時(shí),add EventListener
函數(shù)會(huì)重新注冊。
結(jié)論
不能為了不在 useEffect(fn, deps)
設(shè)置 deps
,使用 useReducer
,并把所有數(shù)據(jù)變更都放在 reducer
中。 本篇文章通過把不修改 reducer
state
的動(dòng)作提前阻止,避免使用此自定義 hooks 的組件發(fā)生不必要渲染,提高代碼復(fù)用性的同時(shí)也兼顧了組件性能。
題外
React.memo
對props
進(jìn)行淺比較,一種組件性能優(yōu)化方式useCallback(fn, deps)
緩存函數(shù)useMemo(() => fn, deps)
緩存昂貴變量- useState(initialState)惰性初始state
,
initialState` 只會(huì)在組件初始渲染中起作用,后續(xù)渲染時(shí)會(huì)被
參考文獻(xiàn)
以上就是React Hooks 之 useReducer 逃避deps后增加組件渲染次數(shù)的陷阱的詳細(xì)內(nèi)容,更多關(guān)于React Hooks useReducer組件渲染的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
react router 4.0以上的路由應(yīng)用詳解
本篇文章主要介紹了react router 4.0以上的路由應(yīng)用詳解,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-09-09react如何修改循環(huán)數(shù)組對象的數(shù)據(jù)
這篇文章主要介紹了react如何修改循環(huán)數(shù)組對象的數(shù)據(jù)問題,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-12-12create-react-app中添加less支持的實(shí)現(xiàn)
這篇文章主要介紹了react.js create-react-app中添加less支持的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-11-11React實(shí)現(xiàn)復(fù)雜搜索表單的展開收起功能
本節(jié)對于需要展開收起效果的查詢表單進(jìn)行概述,主要涉及前端樣式知識。對React實(shí)現(xiàn)復(fù)雜搜索表單的展開-收起功能感興趣的朋友一起看看吧2021-09-09使用?React?Hooks?重構(gòu)類組件的示例詳解
這篇文章主要介紹了如何使用?React?Hooks?重構(gòu)類組件,本文就來通過一些常見示例看看如何使用 React Hooks 來重構(gòu)類組件,需要的朋友可以參考下2022-07-07TypeScript在React項(xiàng)目中的使用實(shí)踐總結(jié)
這篇文章主要介紹了TypeScript在React項(xiàng)目中的使用總結(jié),本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-04-04D3.js(v3)+react 實(shí)現(xiàn)帶坐標(biāo)與比例尺的柱形圖 (V3版本)
這篇文章主要介紹了D3.js(v3)+react 制作 一個(gè)帶坐標(biāo)與比例尺的柱形圖 (V3版本) ,本文通過實(shí)例代碼文字相結(jié)合的形式給大家介紹的非常詳細(xì),需要的朋友可以參考下2019-05-05React模仿網(wǎng)易云音樂實(shí)現(xiàn)一個(gè)音樂項(xiàng)目詳解流程
這篇文章主要介紹了React模仿網(wǎng)易云音樂實(shí)現(xiàn)一個(gè)音樂項(xiàng)目的詳細(xì)流程,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-08-08