您现在的位置是:网站首页> 编程资料编程资料
React Hooks useReducer 逃避deps组件渲染次数增加陷阱_React_
2023-05-24
341人已围观
简介 React Hooks useReducer 逃避deps组件渲染次数增加陷阱_React_
前言
在快乐使用 React Hooks 开发自定义 Hooks 过程中,使用了 useEffect,useReducer,useRef,useCallback 等官方提供的 Hooks,将一些通用逻辑抽离出来,提高代码复用性。
但在组合使用 useEffect,useReducer,React.memo 时,发生了组件在状态未发生变化时触发渲染,因为此动作发生在 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] ); 鼠标每次移动,都会触发 dispatch({ type: "move", x: e.pageX }),在 reducer 函数中,当 !sliding 时,不修改 state 数据原样返回,但是组件仍然进行了渲染。
提前阻止 dispatch 触发
将 sliding 判断移动到 useEffect 中,提前阻止 dispatch 触发,并将 sliding 设置到 useEffect(fn, deps) deps 中,保证监听函数中能取到 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]); 优化后再测试
鼠标仅移动时,sliding 为 false,直接 return,不会触发 dispatch 动作。
好处
避免了组件在 state 未修改时不必要渲染。
坏处
部分处理逻辑被移动到使用自定义 hooks 的组件中,sliding 数据改变时,add EventListener函数会重新注册。
结论
不能为了不在 useEffect(fn, deps) 设置 deps,使用 useReducer,并把所有数据变更都放在 reducer 中。 本篇文章通过把不修改 reducerstate 的动作提前阻止,避免使用此自定义 hooks 的组件发生不必要渲染,提高代码复用性的同时也兼顾了组件性能。
题外
React.memo对props进行浅比较,一种组件性能优化方式useCallback(fn, deps)缓存函数useMemo(() => fn, deps)缓存昂贵变量- useState(initialState)惰性初始state
,initialState` 只会在组件初始渲染中起作用,后续渲染时会被
参考文献
以上就是React Hooks 之 useReducer 逃避deps后增加组件渲染次数的陷阱的详细内容,更多关于React Hooks useReducer组件渲染的资料请关注其它相关文章!
- vue2 d3实现企查查股权穿透图股权结构图效果详解
- React Hooks使用startTransition与useTransition教程示例
- 解决React报错Rendered more hooks than during the previous render
- react hooks UI与业务逻辑分离必要性技术方案
- React Hooks之usePolymerAction抽象代码结构设计理念
- React函数组件useContext useReducer自定义hooks
- Jetpack Compose对比React Hooks API相似度
- react hooks d3实现企查查股权穿透图结构图效果详解
相关内容
- 使用vue-router切换组件时使组件不销毁问题_vue.js_
- JavaScript 字符串新增方法 trim() 的使用说明_javascript技巧_
- vue中百度地图定位及附近搜索功能使用步骤_vue.js_
- JS前端同源策略和跨域及防抖节流详解_JavaScript_
- vuex中getters的基本用法解读_vue.js_
- JavaScript 对象新增方法defineProperty与keys的使用说明_javascript技巧_
- 在nodejs中使用swagger方式_node.js_
- 简单谈一谈Vue中render函数_vue.js_
- Vue实现递归组件的思路与示例代码_vue.js_
- 不能通过IP地址访问VUE项目的问题及解决_vue.js_
