当前位置: 首页 > 科技观察

React中的八个常见错误,如何避免?

时间:2023-03-13 00:35:31 科技观察

大家好,我叫CUGGZ。今天分享一些React开发中常见的错误以及如何避免。了解这些问题背后的细节可以防止犯类似的错误。1.PerformastateupdateafterthecomponentisunmountedCan'tperformaReactstateupdateonanunmountedcomponent报这个错是因为在组件树的某处,状态更新是在unmounted的组件上触发的。也就是说,我们无法在组件销毁后更新状态,以防止内存泄漏。constComponent=()=>{const[data,setData]=useState(null);useEffect(()=>{fetchAsyncData().then((data)=>setData(data));},[]);};比如在请求数据的时候,由于跳转到了B页面,A页面的数据请求还在进行中,但是页面已经销毁了,就会出现这种情况。那么如何解决这个问题呢?有两种方法:(1)在卸载组件时取消异步请求。第一种方法(推荐)是在卸载组件时取消异步请求。一些异步请求库提供了取消异步请求的方法。如果没有使用第三方库,可以使用AbortController取消。当组件被卸载时,这个方法基本上消除了副作用:constComponent=()=>{const[data,setData]=useState(null);useEffect(()=>{constcontroller=newAbortController();fetch(url,{signal:controller.signal}).then((data)=>setData(data));return()=>{controller.abort();}},[]);};(2)trackingcomponent是否挂载?此外,您可以跟踪组件的安装状态。如果没有挂载或者已经卸载,返回false;否则返回真:constComponent=()=>{const[data,setData]=useState(null);constisMounted=useRef(true);useEffect(()=>{fetchAsyncData().then(data=>{if(isMounted.current){setData(data);}});return()=>{isMounted.current=false;};},[]);但是,不推荐使用这种方法。这会保留对未安装组件的引用,并可能导致内存泄漏和性能问题。2.不要使用keyWarning:列表中的每个child在渲染列表时应该有一个唯一的keypropReact开发中最常见的就是遍历数组来渲染组件。在JSX中,您可以使用Array.map将此逻辑嵌入到组件中,并在回调中返回所需的组件。如下:import{Card}from"./Card";constdata=[{id:1,text:"JavaScript"},{id:2,text:"TypeScript"},{id:3,text:"React"}];导出默认函数App(){return({data.map((content)=>(

))}
);}这样,您将收到以下警告:Warning:E??achchildinalistshouldhaveauniquekeyprop,这意味着每个生成的组件都需要被赋予一个唯一的key。因此,给map回调返回的JSX最外层元素添加一个键值,该值应该是一个字符串或者一个数字,并且在这个组件列表中应该是唯一的。exportdefaultfunctionApp(){return({data.map((content)=>(
))}
);}虽然不遵循此要求不会导致应用程序崩溃,但可能会导致一些意外行为。React使用这些键来确定列表中的哪些子项发生了更改,并使用此信息来确定可以重用之前DOM的哪些部分,以及在重新渲染组件时应该重新计算哪些部分。因此,建议添加密钥。3.Hooks的调用顺序错误ReactHook“useXXX”被有条件地调用。ReactHooks必须在每个组件渲染中以完全相同的顺序调用首先看下面的代码:constToggle=()=>{const[isOpen,setIsOpen]=useState(false);如果(isOpen){返回
{}
;}constopenToggle=useCallback(()=>setIsOpen(true),[]);返回{/*...*/};};当isOpen的值为true时,它??会直接返回该div元素。这样在isOpen为true和false时useCallbackHook的调用顺序就不一致了。此时React会警告我们:ReactHook"useCallback"iscalledconditionally。ReactHooks必须在每个组件渲染中以完全相同的顺序调用。这实际上是React官方文档所说的。遵循此规则可确保在每次渲染时以相同顺序调用Hook。上面的代码可以这样修改:constToggle=()=>{const[isOpen,setIsOpen]=useState(false);constopenToggle=useCallback(()=>setIsOpen(true),[]);if(isOpen){return
{/*...*/>
;}返回{/*...*/};};4.useEffect缺少依赖项ReactHookuseEffect缺少依赖项:'XXX'。要么包含它,要么移除依赖数组我们来看看React官网给出的例子:functionExample({someProp}){functiondoSomething(){}useEffect(()=>{doSomething();},[]);在useEffect中定义一个空的依赖数组是不安全的,因为它调用的doSomething函数使用了someProp。这时候会报错:ReactHookuseEffecthasamissingdependency:'XXX'。包括它或删除依赖数组。当props中的someProp发生变化时,函数doSomething的结果发生变化,但是useEffect的依赖数组为空,所以回调中的内容不会被执行。有两种方法可以解决这个问题:在useEffect中声明需要的函数。这种方法适用于只需要调用一次的函数,比如初始化函数:;},[someProp]);}使用useCallback定义依赖,保证函数体在自身依赖发生变化时也会发生变化:])useEffect(()=>{doSomething();},[doSomething]);}5.Toomanyre-renders太多的重新渲染。React限制渲染的次数以防止无限循环这个错误意味着重新渲染的次数过多。React限制渲染次数以防止无限循环。当组件在短时间内有太多状态更新时,就会发生这种情况。无限循环的最常见原因是:直接在渲染中执行状态更新;没有向事件处理程序提供适当的回调。如果遇到这个警告,可以检查组件的这两个方面:constComponent=()=>{const[count,setCount]=useState(0);设置计数(计数+1);//渲染中的状态更新返回({/*onClick没有正确的回调*/}prevCount+1)}>增加那个计数器按钮>);}6.渲染的单条数据是一个对象ObjectsarenotvalidasaReactchild/FunctionsarenotvalidasaReactchild在React中,我们可以在组件中渲染DOM中的很多东西,比如:HTML标签,JSX元素,原始的JavaScript值、JavaScript表达式等。但是对象和函数无法渲染到DOM中,因为这两个值不会被解析为有意义的值。如果渲染了对象或者函数,就会报上面的错误。这个问题的解决方案是简单地检查渲染的内容是否是一个有效值:constComponent=({body})=>(

{/**/>

{/*requiredMake确定body是一个有效的Reactchild*/}{body}
);7.AdjacentJSXelementsmustbewrappedinenclosingtagsAdjacentJSXelementsmustwrappedinAnenclosingtag这个错误的意思是相邻的JSX元素必须被包裹在一个封闭标签中,即必须有一个根元素:constComponent=()=>(<好/><坏/>);从React开发人员的角度来看,这个组件只会在另一个组件内部使用。因此,在他们的心智模型中,从组件返回两个元素是有意义的,因为无论外部元素是在此组件中定义还是在父组件中定义,最终的DOM结构都是相同的。然而,React不能做这个假设。该组件可能会在根目录中使用并破坏应用程序,因为它会导致无效的DOM结构。因此,您应该始终将组件返回的多个JSX元素包装在单个封闭标记中。它可以是一个元素、一个组件或ReactFragment:constComponent=()=>();或者直接用空标签包裹两个JSX元素:constComponent=()=>(<>/>);8.使用旧状态首先看一个计数器的例子:constInccreaser=()=>{const[count,setCount]=useState(0);constincrease=useCallback(()=>{setCount(count+1);},[count]);consthandleClick=()=>{增加();增加();增加();};return(<>+
Counter:{count}
);}这里的handleClick方法会点击按钮然后执行增加的操作状态变量计数三次。那么一次点击会增加3吗?但事实上并非如此。点击按钮后,计数只会加1。问题是我们点击按钮时,相当于下面的操作:consthandleClick=()=>{setCount(count+1);设置计数(计数+1);设置计数(计数+1);};当第一次调用setCount(count+1)没有问题时,会更新count为1。接下来,当第二次和第三次调用setCount时,count仍然使用旧状态(count为0),因此count也计算为1。出现这种情况的原因是状态变量直到下一次渲染才更新.这个问题的解决方案是使用一个函数来更新状态:constIncexer=()=>{const[count,setCount]=useState(0);constincrease=useCallback(()=>{setCount(count=>count+1);},[count]);consthandleClick=()=>{增加();增加();增加();};return(<>+
Counter:{count}
);}这样修改后,React可以获取到最新的值,当按钮被点击,每次增加3。所以你需要记住:setValue(prevValue=>prevValue+someResult)