当前位置: 首页 > Web前端 > JavaScript

如何解决React.useEffect()

时间:2023-03-27 18:25:13 JavaScript

的死循环。本文已收录到GitHubhttps://github.com/qq449245884/xiaozhi,里面有完整的测试站点、资料和我的一线厂商访谈系列文章。useEffect()主要用于管理副作用,例如通过网页抓取、直接操作DOM、启动和结束计时器。虽然useEffect()和useState(管理状态的方法)是最常用的钩子,但需要一些时间才能熟悉并正确使用它们。使用useEffect()时可能遇到的一个陷阱是组件渲染的无限循环。在本文中,我们将探讨创建无限循环的常见场景以及如何避免它们。1.死循环和副作用更新状态假设我们有一个功能组件,里面有一个输入元素,该组件的功能是统计输入变化的次数。我们将这个组件命名为CountInputChanges,大致内容如下:functionCountInputChanges(){const[value,setValue]=useState('');const[count,setCount]=useState(-1);useEffect(()=>setCount(count+1));constonChange=({target})=>setValue(target.value);return(

变化次数:{count}
)}是受控组件。value变量保存输入输入的值,当用户输入输入时,onChange事件处理程序更新值状态。这里使用useEffect()来更新计数变量。useEffect(()=>setCount(count+1))每次组件因用户输入而重新渲染时都会更新计数器。因为使用useEffect(()=>setCount(count+1))时没有依赖参数,所以()=>setCount(count+1)会在组件每次渲染后执行回调。你觉得这样写有问题吗?打开demo自己试一下:https://codesandbox.io/s/infi...运行后会发现count状态变量不受控制的增加,即使输入什么都不输入,这是一个死循环.问题在于如何使用useEffect():useEffect(()=>setCount(count+1));它会生成组件重新渲染的无限循环。初始渲染后,useEffect()执行更新状态的副作用回调函数。状态更新触发重新渲染。重新渲染后,useEffect()执行副作用回调并再次更新状态,这将再次触发重新渲染。1.1可以通过正确管理useEffect(callback,dependencies)依赖参数来解决具有依赖关系的无限循环。由于我们希望计数在值更改时递增,因此我们可以简单地使值成为副作用的依赖项。从'反应'导入{useEffect,useState};函数CountInputChanges(){const[value,setValue]=useState('');const[count,setCount]=useState(-1);useEffect(()=>setCount(计数+1),[值]);constonChange=({target})=>setValue(target.value);return(
变化次数:{count}
);}Add[value]asuseEffect的依赖项,因此仅当[value]更改时才会更新计数状态变量。这样做可以解决无限循环。1.2使用ref除了依赖,我们还可以使用useRef()来解决这个问题。这个想法是更新Ref不会触发组件的重新渲染。import{useEffect,useState,useRef}from"react";functionCountInputChanges(){const[value,setValue]=useState("");constcountRef=useRef(0);useEffect(()=>countRef.current++);constonChange=({target})=>setValue(target.value);return(
变化次数:{countRef.current}
);}useEffect(()=>countRef.current++)每次由于值变化而重新渲染后,countRef.current++将返回。引用更改本身不会触发组件重新渲染。2.无限循环和新对象引用即使正确设置了useEffect()依赖项,在使用对象作为依赖项时也要小心。例如下面的组件CountSecrets,在input中监听用户输入的单词。一旦用户输入特殊词“secret”,“secret”的计数将增加1。import{useEffect,useState}from"react";functionCountSecrets(){const[secret,setSecret]=useState({value:"",countSecrets:0});useEffect(()=>{if(secret.value==='secret'){setSecret(s=>({...s,countSecrets:s.countSecrets+1}));}},[秘密]);constonChange=({target})=>{setSecret(s=>({...s,value:target.value}));};return(
秘密数量:{secret.countSecrets}
);}打开demo(https://codesandbox.io/s/infi...)自己去试试,目前输入secret,secret.countSecrets的值开始不受控制的增长。这是一个无限循环问题。为什么?秘密对象用作useEffect(...,[secret])。在副作用回调函数中,只要输入值等于secret,就会调用更新函数setSecret(s=>({...s,countSecrets:s.countSecrets+1}));这会增加countSecrets的值,但也会创建一个新对象。秘密现在是一个新对象,并且依赖关系已更改。所以useEffect(...,[secret])被再次调用,副作用是更新状态并再次创建一个新的秘密对象,等等。JavaScript中的两个对象只有在引用完全相同的对象时才相等。2.1避免使用对象作为依赖项解决循环创建新对象造成的无限循环问题的最好方法是避免在useEffect()的依赖项参数中使用对象引用。letcount=0;useEffect(()=>{//一些逻辑},[count]);//好!letmyObject={prop:'Value'};useEffect(()=>{//一些逻辑},[myObject]);//不好!useEffect(()=>{//一些逻辑},[myObject.prop]);//好的!为了解决组件的无限循环问题,useEffect(...,[secret]))变为useEffect(...,[secret.value])。只需在secret.value变化时调用副作用回调即可,这里是固定代码:,countSecrets:0});useEffect(()=>{if(secret.value==='secret'){setSecret(s=>({...s,countSecrets:s.countSecrets+1}));}},[secret.value]);constonChange=({target})=>{setSecret(s=>({...s,value:target.value}));};return(
秘密数量:{secret.countSecrets}
);}3总结useEffect(callback,deps)是一个Hook,在组件渲染后执行回调(副作用)。如果不注意副作用是如何工作的,就会触发组件渲染的无限循环。生成无限循环的常见情况是在不指定任何依赖参数的情况下更新副作用中的状态useEffect(()=>{//无限循环!setState(count+1);});避免无限循环的有效方法是正确设置依赖项:useEffect(()=>{//没有无限循环setState(count+1);},[whenToUpdateValue]);另外,还可以使用Ref,更新Ref不会触发重新渲染:useEffect(()=>{//不会死循环countRef.current++;});无限循环的另一种常见方法是使用一个对象作为useEffect()的依赖项,并在副作用中更新该对象(有效地创建一个新对象)useEffect(()=>{//无限循环!setObject({...object,prop:'newValue'})},[object]);避免使用对象作为依赖,只使用特定的属性(最终结果应该是一个原始值):useEffect(()=>{//没有无限循环setObject({...object,prop:'newValue'})},[object.whenToUpdateProp]);在使用useEffect()的时候,你也知道有没有其他方法造成死循环陷阱?~完了,我是小智,下期见~代码部署后可能出现的bug,无法实时得知。之后为了解决这些bug,我花了很多时间在日志调试上。顺便在这里推荐一个好用的bug监控工具Fundebug。原文:https://dmitripavlutin.com/re...交流有梦想,有干货,微信搜索【走向世界的大招】关注这位凌晨还在洗碗的洗碗智慧。本文GitHubhttps://github.com/qq44924588...已收录,有完整的测试站点、资料和我的一线厂商访谈系列文章。