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

目录
  • 前言
  • 自定义 Hooks 简单实现
  • 在组件中使用自定义 Hooks
  • 提前阻止 dispatch 触发
    • 优化后再测试
  • 结论
  • 题外

前言

在快乐使用 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组件渲染的资料请关注我们其它相关文章!

(0)

相关推荐

  • React hooks useState异步问题及解决

    目录 React Hooks useState异步问题 原因 解决方法 React中useState异步更新小坑 问题点 React Hooks useState异步问题 最近在开发中遇到一个问题 我接口请求回来的数据 用useState存储起来. 但是我后面 去改变这个数据的时候每次拿到都是上次的数据没办法及时更新. 原因 useState 返回的更新状态方法是异步的,要在下次重绘才能获取新值.不要试图在更改状态之后立马获取状态. 解决方法 应该使用useRef 存储这个数据,在useEffe

  • React Hooks钩子中API的使用示例分析

    目录 hooks是什么 Hooks的作用 使用Hooks组件前后开发模式的对比 Hooks使用策略 为什么要有Hooks useState useEffect使用 useEffect依赖项 使用情景 useMemo使用 useMemo缓存组件方式 useMemo和useEffect的区别 useCallback使用 useContext使用 useRef使用 为什么在函数组件中无法使用ref 如何在类组件中使用ref属性 自定义hooks hooks是什么 hooks理解字面意思就是钩子,是一些

  • React Hooks之usePolymerAction抽象代码结构设计理念

    目录 背景 设计理念 抽象代码结构 声明与定义 用法 高级用法 拆分/合并action API useSetState getAction polymerAction usePolymerActionState mergePolymerAction 背景 随着React Hooks 普及起来,组件逻辑写法变成函数式:逻辑函数声明.函数代码结构,随着逻辑增加复杂,代码组织混乱,函数声明散乱: 最关键的一点,每个人对代码理解不一样,有的喜欢在一个function 写大段逻辑,有的喜欢拆分逻辑很细,

  • React组件学习之Hooks使用

    目录 一.前言 二.React Hooks 2.1 useState 2.2 useEffect 2.3 useMemo 2.4 useCallback 2.5 useContext 2.6 useRef 三.总结 一.前言 react组件分为类(class)组件和函数(function)组件. class 组件是通过继承模版类(Component.PureComponent)的方式开发新组件的,继承是 class 本身的特性,它支持设置 state,会在 state 改变后重新渲染,可以重写一

  • react中(含hooks)同步获取state值的方式

    目录 react(含hooks)同步获取state值 环境 代码示例 异步写成同步的方法 react hooks常用方法 1.useState 2.useEffect 3.useContext上下文传值 4.useReducer 5.useMemo 6.useRef react(含hooks)同步获取state值 环境 "dependencies": {     "babel-plugin-transform-decorators-legacy": "^1

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

    目录 前言 自定义 Hooks 简单实现 在组件中使用自定义 Hooks 提前阻止 dispatch 触发 优化后再测试 结论 题外 前言 在快乐使用 React Hooks 开发自定义 Hooks 过程中,使用了 useEffect,useReducer,useRef,useCallback 等官方提供的 Hooks,将一些通用逻辑抽离出来,提高代码复用性. 但在组合使用 useEffect,useReducer,React.memo 时,发生了组件在状态未发生变化时触发渲染,因为此动作发生在

  • React中immutable的UI组件渲染性能详解

    目录 引言 UI组件渲染性能 方案一:shallow compare 方案二:直接对前后的对象进行deepCompare 总结: 引言 react 一直遵循UI = fn(state) 的原则,有时候我们的state却和UI不同步 有时候组件本身在业务上不需要渲染,却又会再一次re-render.之前在项目中遇到的一些问题,这里做一个简单的分析,大家可以一起交流一下 UI组件渲染性能 react每次触发页面的更新可大致分成两步: render(): 主要是计算v-dom的diff commit阶

  • 使用 React Hooks 重构类组件的示例详解

    目录 1. 管理和更新组件状态 2. 状态更新后的操作 3. 获取数据 4. 卸载组件时清理副作用 5.  防止组件重新渲染 6. Context API 7. 跨重新渲染保留值 8. 如何向父组件传递状态和方法? 9. 小结 最初,在 React 中可以使用 createClass 来创建组件,后来被类组件所取代.在 React 16.8 版本中,新增的 Hooks 功能彻底改变了我们编写 React 程序的方式,使用 Hooks 可以编写更简洁.更清晰的代码,并为创建可重用的有状态逻辑提供了

  • React Hooks使用常见的坑

    React Hooks 是 React 16.8 引入的新特性,允许我们在不使用 Class 的前提下使用 state 和其他特性.React Hooks 要解决的问题是状态共享,是继 render-props 和 higher-order components 之后的第三种状态逻辑复用方案,不会产生 JSX 嵌套地狱问题. 为什么会有Hooks? 介绍Hooks之前,首先要给大家说一下React的组件创建方式,一种是类组件,一种是纯函数组件,并且React团队希望,组件不要变成复杂的容器,最好

  • React Hooks - useContetx和useReducer的使用实例详解

    目录 useContetx的使用 useReducer的使用 useContetx的使用 在之前的开发中,我们要在组件中使用共享的Context有两种方式: 类组件可以通过 类名.contextType = MyContext 的方式,在类中获取context; 多个Context或者在函数式组件中通过 MyContext.Consumer 方式共享context; 但是多个Context共享时的方式会存在大量的嵌套(会导致代码阅读性非常差): Context Hook允许我们通过Hook来直接

  • 详解如何使用React Hooks请求数据并渲染

    前言 在日常的开发中,从服务器端异步获取数据并渲染是相当高频的操作.在以往使用React Class组件的时候,这种操作我们已经很熟悉了,即在Class组件的componentDidMount中通过ajax来获取数据并setState,触发组件更新. 随着Hook的到来,我们可以在一些场景中使用Hook的写法来替代Class的写法.但是Hook中没有setState.componentDidMount等函数,又如何做到从服务器端异步获取数据并渲染呢?本文将会介绍如何使用React的新特性Hook

  • 基于react hooks,zarm组件库配置开发h5表单页面的实例代码

    最近使用React Hooks结合zarm组件库,基于js对象配置方式开发了大量的h5表单页面.大家都知道h5表单功能无非就是表单数据的收集,验证,提交,回显编辑,通常排列方式也是自上向下一行一列的方式显示 , 所以一开始就考虑封装一个配置化的页面生成方案,目前已经有多个项目基于此方式配置开发上线,思路和实现分享一下. 使用场景 任意包含表单的h5页面(使用zarm库,或自行适配自己的库) 目标 代码实现简单和简洁 基于配置 新手上手快,无学习成本 老手易扩展和维护 写之前参考了市面上的一些方案

  • React如何将组件渲染到指定DOM节点详解

    前言 众所周知React优点之一就是他的API特别简单.通过render 方法返回一个组件的基本结构,如同一个简单的函数,就可以得到一个可以复用的react组件.但是有时候还是会有些限制的,尤其是他的API中,不能控制组件所应该渲染到的DOM节点,这就让一些弹层组件很难控制.当父元素设置为overflow:hidden 的时候,问题就会出现了. 例如就像下面的这样: 我们实际期待的效果是这样的: 幸运的是,虽然不是很明显,但有一个相当优雅的方式来绕过这个问题.我们学到的第一个react函数是re

  • React 组件渲染和更新的实现代码示例

    最近一直写React,慢慢就对里面的一些实现很好奇.最好奇的就是自定义标签的实现和this.setState的实现.这里不分析JSX是如何解析的,所有组件都用ES5方式编写. 组件渲染 渲染时候,我们会调用render方法.类似下面这样: var SayHi = React.createClass({ getInitialState: function() { return {verb: 'say:'}; }, componentWillMount: function() { console.l

随机推荐