返回顶部
首页 > 资讯 > 精选 >React之useEffect依赖引用类型问题怎么解决
  • 781
分享到

React之useEffect依赖引用类型问题怎么解决

2023-07-05 12:07:47 781人浏览 薄情痞子
摘要

本文小编为大家详细介绍“React之useEffect依赖引用类型问题怎么解决”,内容详细,步骤清晰,细节处理妥当,希望这篇“React之useEffect依赖引用类型问题怎么解决”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来

本文小编为大家详细介绍“React之useEffect依赖引用类型问题怎么解决”,内容详细,步骤清晰,细节处理妥当,希望这篇“React之useEffect依赖引用类型问题怎么解决”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。

    问题提出

    const Issue = function () {  const [count, setCount] = useState(0);  const [person, setPerson] = useState({ name: 'Alice', age: 15 });  const [array, setArray] = useState([1, 2, 3]);   useEffect(() => {    console.log('Component re-rendered by count');  }, [count]);   useEffect(() => {    console.log('Component re-rendered by person');  }, [person]);   useEffect(() => {    console.log('Component re-rendered by array');  }, [array]);   return (    <div>      <p>You clicked {count} times</p>      <button onClick={() => setCount(1)}>Update Count</button>      <button onClick={() => setPerson({ name: 'Bob', age: 30 })}>Update Person</button>      <button onClick={() => setArray([1, 2, 3, 4])}>Update Array</button>    </div>  );};

    在这个案例中,初始化了三个状态,和对应的三个副作用函数useEffect,理想状态是状态的值更新时才触发useEffect。
    多次点击Update Count更新State,因为更新后的值还是1,所以第一个useEffect执行第一次后不会重复执行,这符合预期。但是重复点击Update Person和Update Array时,却不是这样,尽管值相同,但useEffect每一次都会触发。当useEffect中的副作用计算量较大时,必然会引起性能问题。

    原因追溯

    为了追溯这个原因,可以首先熟悉一下useEffect的源码

    function useEffect(create, deps) {  const fiber = get();  const { alternate } = fiber;   if (alternate !== null) {    const oldProps = alternate.memoizedProps;    const [oldDeps, hasSameDeps] = areHookInputsEqual(deps, alternate.memoizedDeps);     if (hasSameDeps) {      pushEffect(fiber, oldProps, deps);      return;    }  }   const newEffect = create();   pushEffect(fiber, newEffect, deps);} function areHookInputsEqual(nextDeps, prevDeps) {  if (prevDeps === null) {    return false;  }   for (let i = 0; i < prevDeps.length && i < nextDeps.length; i++) {    if (Object.is(nextDeps[i], prevDeps[i])) {      continue;    }     return false;  }   return true;}

    在上面的代码中,我们着重关注areHookInputsEqual的实现,这个函数对比了前后两次传入的依赖项,决定了后续副作用函数create()是否会执行。可以明显看到,useEffect对于依赖项执行的是浅比较,即Object.is (arg1, arg2),这可能是出于性能考虑。对于原始类型这没有问题,但对于引用类型(数组、对象、函数等),这意味着即使内部的值保持不变,引用本身也会发生变化,导致 useEffect执行副作用。

    方案探索

    1.饮鸩止渴

    缝缝补补只是为了等一个人替你推倒重盖

    最直接的思路是把useEffect的依赖项从引用类型换成基本类型:

      useEffect(() => {    console.log('Component re-rendered by person');  }, [JSON.stringify(person)]);   useEffect(() => {    console.log('Component re-rendered by array');  }, [jsON.stringify(array)]);

    表面上可行,实际后患无穷(具体参考JSON.stringify为什么不能用来深拷贝),为了避坑而挖另外的坑,显然不是我们期待的解决方案。
    对比之下,这样的写法可以容忍,但是person对象如果增加了其他属性,你要确保自己还记得更新依赖,否则依然是掩盖问题。

    useEffect(() => {  console.log('Component re-rendered by person');}, [person.name, person.age]);

    2.前置拦截

    第二种思路:

    在你决定要出手之前,我已经帮你决定了 &mdash;&mdash; 格林公式引申公理

    我们可以把问题尽可能前置,手动加一层深对比,如何发现引用值没有变化,就不执行状态更新的逻辑,也就不会触发useEffect重复执行。

    <button onClick={() => {    const newPerson = { name: 'Bob', age: 18 };    if (!isEqual(newPerson, person)) {      setPerson(newPerson)}    }  }>Update person</button>

    但这样显然不太优雅,且每一次写setState时心智负担太重,对比逻辑可不可以封装起来。

    3.他山之石

    实际上自定义的Hooks就是为了解决方法级别的逻辑复用,这里我们利用useRef绑定的值可以跨渲染周期的特点,实现一个自定义的useCompare。

    const useCompare = (value, compare) => {  const ref = useRef(null);  if (!compare(value, ref.current)) {    ref.current = value;  }  return ref.current;}

    经过ref记录的上一次结果,我们同时拥有了前后两次更新的状态,如果发现值不同,再让ref绑定新的引用类型地址。

    import { isEqual } from 'lodash'; const comparePerson = useCompare(person, isEqual); useEffect(() => {    console.log('Component re-rendered by comparePerson');}, [comparePerson]); // 重复执行useEffect(() => {  console.log('Component re-rendered by person');}, [person]);

    需要注意的是,这里使用了lodash的isEqual函数实现深对比,看似省心实际是一个成本极其不稳定的选择,如果对象过于庞大,可能得不偿失,可以传入简化的compare函数,有取舍的比较常变的key值。
    而且每次又到单独调用useCompare生成新的对象,这里的逻辑也值得被封装。

    4.回归本质

    停止曲线救国,直面问题本身。

    说了这么多,实际还是useEffect中对比逻辑问题,本着支持拓展但不支持修改的原则,我们需要支持一个新的useEffect支持深度对比。我们将useRef实现的记忆引用传入useEffect的对比逻辑中:

    import { useEffect, useRef } from 'react';import isEqual from 'lodash.isequal'; const useDeepCompareEffect = (callback, dependencies, compare) => {  // 默认的对比函数采用lodash.isEqual, 支持自定义  if (!compare) compare = isEqual;  const memoizedDependencies = useRef([]);  if (!compare (memoizedDependencies.current, dependencies)) {    memoizedDependencies.current = dependencies;  }  useEffect(callback, memoizedDependencies.current);}; export default useDeepCompareEffect;  function App({ data }) {  useDeepCompareEffect(() => {    // 这里的代码只有在 data 发生深层级的改变时才会执行    console.log('data 发生了改变', data);  }, [data]);   return <div>Hello World</div>;}

    考虑到前文提到的复杂对象的深对比隐患,我依然结和个人意志,在useDeepCompareEffect中加了一个可选参数compare函数,把isEqual作为一种默认模式。

    读到这里,这篇“React之useEffect依赖引用类型问题怎么解决”文章已经介绍完毕,想要掌握这篇文章的知识点还需要大家自己动手实践使用过才能领会,如果想了解更多相关内容的文章,欢迎关注编程网精选频道。

    --结束END--

    本文标题: React之useEffect依赖引用类型问题怎么解决

    本文链接: https://lsjlt.com/news/351564.html(转载时请注明来源链接)

    有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

    猜你喜欢
    • React之useEffect依赖引用类型问题怎么解决
      本文小编为大家详细介绍“React之useEffect依赖引用类型问题怎么解决”,内容详细,步骤清晰,细节处理妥当,希望这篇“React之useEffect依赖引用类型问题怎么解决”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来...
      99+
      2023-07-05
    • React避坑指南之useEffect依赖引用类型问题分析
      目录前言问题提出原因追溯方案探索1.饮鸩止渴2.前置拦截3.他山之石4.回归本质总结前言 如果你是一个入行不久的前端开发,面试中多半会遇到一个问题: 你认为使用React要注意些什么...
      99+
      2023-03-14
      React useEffect 依赖引用类型 React避坑指南 React useEffect 依赖
    • React中useEffect使用问题怎么解决
      本篇内容介绍了“React中useEffect使用问题怎么解决”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!前言最近看了一下 ant-des...
      99+
      2023-07-02
    • Go modules replace解决Go依赖引用问题
      为什么会用到 replace 取名是一个很有讲究的事情,但每个人都不一样。 一开始,我写了一个 A 项目,代码仓名称为 project-alpha但 go.mod 里的 packag...
      99+
      2024-04-02
    • Node Sass依赖问题怎么解决
      这篇文章主要介绍“Node Sass依赖问题怎么解决”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Node Sass依赖问题怎么解决”文章能帮助大家解决问题。node-sassn...
      99+
      2023-07-06
    • 怎么解决Spring循环依赖问题
      本篇内容介绍了“怎么解决Spring循环依赖问题”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!前言循环依赖...
      99+
      2024-04-02
    • springboot怎么解决循环依赖问题
      在Spring Boot中解决循环依赖问题,可以尝试以下几种方法:1. 使用构造器注入:将循环依赖的对象注入到构造器中,并且使用`@...
      99+
      2023-09-27
      springboot
    • Spring循环依赖问题怎么解决
      在Spring中,循环依赖问题是指两个或多个bean之间出现相互依赖的情况。由于Spring容器默认使用单例模式管理bean,因此循...
      99+
      2023-08-31
      Spring
    • gradle依赖冲突问题怎么解决
      在Gradle中,依赖冲突通常是由于不同的依赖项引入了相同的库的不同版本造成的。以下是一些解决依赖冲突问题的方法:1. 使用`gra...
      99+
      2023-10-11
      gradle
    • spring怎么解决相互依赖问题
      在Spring中,可以通过以下几种方式解决相互依赖问题:1. 构造函数注入:使用构造函数将依赖项作为参数传递给类的构造函数,从而实现...
      99+
      2023-08-16
      spring
    • ubuntu怎么解决libsqlite3-0依赖的问题
      这篇文章给大家分享的是有关ubuntu怎么解决libsqlite3-0依赖的问题的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。fan@fan:sqlite$ sudo apt-get install sqlite...
      99+
      2023-06-09
    • 怎么解决ubuntu13.04安装chrome依赖问题
      这篇文章主要介绍了怎么解决ubuntu13.04安装chrome依赖问题,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。Ubuntu 13.04 用户安装 chrome 会遇到...
      99+
      2023-06-13
    • maven依赖${xxx.version}报错问题怎么解决
      这篇文章主要介绍“maven依赖${xxx.version}报错问题怎么解决”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“maven依赖${xxx.version}报错问题怎么解决”文章能帮助大家解...
      99+
      2023-06-26
    • SpringBoot中的Mybatis依赖问题怎么解决
      本篇内容主要讲解“SpringBoot中的Mybatis依赖问题怎么解决”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“SpringBoot中的Mybatis依赖问题怎么解决”吧!Pom导入依赖&...
      99+
      2023-06-30
    • 如何解决Vue依赖收集引发的问题
      这篇文章主要为大家展示了“如何解决Vue依赖收集引发的问题”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“如何解决Vue依赖收集引发的问题”这篇文章吧。问题背景在...
      99+
      2024-04-02
    • 怎么在spring中解决循环依赖问题
      怎么在spring中解决循环依赖问题?针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。setter singleton循环依赖使用SingleSetterBeanA依赖Sing...
      99+
      2023-06-08
    • java怎么解决依赖版本冲突问题
      在Java中解决依赖版本冲突问题有以下几种方法: 更新依赖版本:可以尝试更新冲突的依赖版本,看是否有新版本解决了冲突问题。可以通...
      99+
      2023-10-27
      java
    • idea maven依赖引入失效无法正常导入依赖问题的解决方法
      目录前言1.File —>Project Structure2. Libraries —>点击依赖列表左上角的“+” &m...
      99+
      2023-05-16
      idea maven 依赖 idea maven依赖无法导入 idea无法导入本地maven依赖
    • 怎么解决React useEffect钩子带来的无限循环问题
      本篇内容主要讲解“怎么解决React useEffect钩子带来的无限循环问题”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“怎么解决React useEffect钩子带来的无...
      99+
      2023-07-02
    • maven多版本依赖冲突问题怎么解决
      Maven的多版本依赖冲突问题可以通过以下几种方式解决:1. 排除依赖:在项目的pom.xml文件中,可以使用``标签排除某个依赖的...
      99+
      2023-09-23
      maven
    软考高级职称资格查询
    编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
    • 官方手机版

    • 微信公众号

    • 商务合作