React Hooks useReducer 逃避deps组件渲染次数增加陷阱

 更新时间:2022年09月06日 16:45:22   作者:qwer  
这篇文章主要介绍了React Hooks 之 useReducer 逃避deps后增加组件渲染次数的陷阱详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

前言

在快乐使用 React Hooks 开发自定义 Hooks 过程中,使用了 useEffectuseReduceruseRefuseCallback 等官方提供的 Hooks,将一些通用逻辑抽离出来,提高代码复用性。

但在组合使用 useEffectuseReducerReact.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]);

优化后再测试

鼠标仅移动时,slidingfalse,直接 return,不会触发 dispatch 动作。

好处

避免了组件在 state 未修改时不必要渲染。

坏处

部分处理逻辑被移动到使用自定义 hooks 的组件中,sliding 数据改变时,add EventListener函数会重新注册。

结论

不能为了不在 useEffect(fn, deps) 设置 deps,使用 useReducer,并把所有数据变更都放在 reducer 中。 本篇文章通过把不修改 reducer state 的动作提前阻止,避免使用此自定义 hooks 的组件发生不必要渲染,提高代码复用性的同时也兼顾了组件性能。

题外

  • React.memoprops 进行浅比较,一种组件性能优化方式
  • useCallback(fn, deps) 缓存函数
  • useMemo(() => fn, deps) 缓存昂贵变量
  • useState(initialState)惰性初始stateinitialState` 只会在组件初始渲染中起作用,后续渲染时会被

参考文献

React 官方文档

以上就是React Hooks 之 useReducer 逃避deps后增加组件渲染次数的陷阱的详细内容,更多关于React Hooks useReducer组件渲染的资料请关注脚本之家其它相关文章!

相关文章

  • React如何避免重渲染

    React如何避免重渲染

    这篇文章主要介绍了React如何避免重渲染,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-04-04
  • 如何使用React构建一个掷骰子的小游戏

    如何使用React构建一个掷骰子的小游戏

    这篇文章主要介绍了如何使用React构建一个掷骰子的小游戏,本文通过实例代码给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧
    2024-12-12
  • React星星评分组件的实现

    React星星评分组件的实现

    评分插件在购物的应用中经常可以看得到,但是用着别人的总是没有自己写的顺手,本文就使用React实现星星评分组件,感兴趣的可以了解一下
    2021-06-06
  • react dva实现的代码

    react dva实现的代码

    dva是一个基于redux和redux-saga的数据流方案,然后为了简化开发体验,dva额外内置了react-router,fetch,可以激烈为一个轻量级的应用框架,这篇文章主要介绍了react dva实现,需要的朋友可以参考下
    2021-11-11
  • react-router-domV6嵌套路由实现详解

    react-router-domV6嵌套路由实现详解

    这篇文章主要为大家介绍了react-router-domV6嵌套路由实现详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-01-01
  • 解读react的onClick自动触发等相关问题

    解读react的onClick自动触发等相关问题

    这篇文章主要介绍了解读react的onClick自动触发等相关问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-02-02
  • 详解React中setState回调函数

    详解React中setState回调函数

    这篇文章主要介绍了详解React中setState回调函数,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-06-06
  • React中路由的参数传递路由的配置文件详解

    React中路由的参数传递路由的配置文件详解

    路由的配置文件目前我们所有的路由定义都是直接使用Route组件,并且添加属性来完成的,路由的参数传递有二种方式这,两种方式在Router6.x中都是提供的hook函数的API, 类组件需要通过高阶组件的方式使用,本文通过示例代码详解讲解,需要的朋友参考下吧
    2022-11-11
  • react+zarm实现底部导航栏的示例代码

    react+zarm实现底部导航栏的示例代码

    本文主要介绍了react + zarm 实现底部导航栏的示例代码,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-05-05
  • react-native封装插件swiper的使用方法

    react-native封装插件swiper的使用方法

    这篇文章主要介绍了react-native封装插件swiper的使用方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-03-03

最新评论