useState的异步问题
react 函数组件
常见的钩子函数:useState、useEffect
useState的异步
useState作为最常见的一个hook,在使用中总是会出现各种坑,最明显的就是 useState 更新异步的问题。
- 问题描述:把接口返回的数据,使用 useState 储存起来,但是当后面去改变这个数据的时候,每次拿到的都是上次的数据,无法实时更新。或者我们在函数内部使用 setState ,然后立即打印 state,打印的结果还是第一次的state 的值。
- 原因:useState 返回的更新对象的方法是异步的,要在下次重绘才能获取新值,不要试图在更改状态之后立即获取状态。
- 控制台报错:
- 代码,函数组件:
import React,{ useState, useEffect }from'react';import styles from'./index.less';exportdefaultfunctionindex({ content ={}}){const[data, setData]=useState([]);useEffect(()=>{if(content !==null){const myModityData = content.title.split('***');setData(myModityData);}},[content.title]);return(<div className={styles.home_box}><div className={styles.title}>{data[0]}</div><div className={styles.titleBottom}>{data[1]}</div><p className={styles.des}>{content.des}</p></div>);}
原因总结:
这是因为React里事件分为合成事件和原生事件,合成事件和钩子函数都是异步的,原生事件是同步的。因为useState是异步事件,所以会出现不能及时获取到最新值的情况。
useState 返回的更新状态方法是异步的,要在下次重绘才能获取新值。不要试图在更改状态之后立马获取状态。
注意:useEffect的第二个参数,我表明监听content.title但还是不行,因为在React的函数组件中使用useState进行数据存储,导致数据异步,不能及时获取当前最新的数据。
解决办法思路:
- 办法一:先使用useRef进行存储数据,再使用useEffect监听数据变化,并进行更新。 在之后需要使用 info 数据的地方只需要获取 infoRef.current 即可获取最新的 info 数据内容。
import React,{ useState, useEffect, useRef }from'react';constIndex=()=>{const[info, setInfo]=useState()const infoRef =useRef()useEffect(()=>{
infoRef.current = info
},[info])}
- 办法二:使用回调函数更改数据
- useState 使用的两种方式 我们知道,useState中的 [ ] 是一个解构运算,第一个是设置的值,第二个是用来改变 state 的方法。 一般情况下,我们使用第一种方式即可,但在某些特殊情况下,第一种方式获取到的值不是最新设置的。
const[data, setData]=useState(1)setData(data +1)
const[data, setData]=useState(0)setData((prev)=> prev +1);// prev 是data 改变之前的值,return 返回的值会作为新状态覆盖data
原文链接:React hooks中 useState踩坑——异步问题
实际解决办法:
办法一:页面能渲染出来,但控制台报错,监听会一直存在,很耗能
控制台报错:
中文解释:警告:超过了最大更新深度。当组件在useEffect内部调用setState时,可能会发生这种情况,但useEffect要么没有依赖关系数组,要么依赖关系在每次渲染时都会发生变化。
代码:
完整代码:
constUAVEdge=(props)=>{const{ UAVEdgeModel, dispatch }= props;const{ productLists, typeId, page }= UAVEdgeModel;const[data, setData]=useState(productLists);const myRef =useRef();useEffect(()=>{
myRef.current = data;const myDeleteData = myRef.current.slice(0,1);setData(myDeleteData);},[data]);useEffect(()=>{dispatch({type:'UAVEdgeModel/getProductList',});},[]);return(<><Banner content={uavDate}/><Content content={data}/><Content02 content={productLists}/>{/* <Swiper /> */}</>);};exportdefaultconnect(({ UAVEdgeModel })=>({
UAVEdgeModel,}))(UAVEdge);
办法二:useState(使用回调函数)
控制台报错:
中文解释:
未捕获错误:重新渲染过多。React限制渲染次数,以防止无限循环。
代码:
完整代码:
import React,{ useState, useEffect }from'react';import styles from'./index.less';exportdefaultfunctionindex({ content =[]}){const[data, setData]=useState(content);setData((prev)=>{const myDeleteData = prev.slice(1,5);return myDeleteData
})return(<div className={styles.home_box}><div className={styles.home_container}>{data.map((item)=>{return(<div className={styles.edge_box} key={item.id}><div className={styles.boxContainer}><img src={item.img} alt="一张图"/><div className={styles.title}>{item.title}</div><p className={styles.des}>{item.des}</p></div></div>);})}</div></div>);}
办法三:办法一的改进
代码:
完整代码:
import React,{ useState, useEffect, useRef }from'react';import styles from'./index.less';exportdefaultfunctionindex({ content =[]}){const[data, setData]=useState(content);const myRef =useRef();useEffect(()=>{const myDeleteData = data.slice(1,5);
myRef.current = myDeleteData;},[data]);return(<div className={styles.home_box}><div className={styles.home_container}>{myRef.current?.map((item)=>{return(<div className={styles.edge_box} key={item.id}><div className={styles.boxContainer}><img src={item.img} alt="一张图"/><div className={styles.title}>{item.title}</div><p className={styles.des}>{item.des}</p></div></div>);})}</div></div>);}
办法四:组件传参的时候,传数据
思路:组件传参的时候,传数据
控制台报错:
中文解释:
需要独一无二的key。可是我明明已经给了,为什么?
接口返回的数据:
函数组件传参,代码:
函数组件传参,完整代码:
constUAVEdge=(props)=>{const{ UAVEdgeModel, dispatch }= props;const{ productLists, typeId, page }= UAVEdgeModel;useEffect(()=>{dispatch({type:'UAVEdgeModel/getProductList',});},[]);return(<><Banner content={uavDate}/><Content content={[productLists[0]||{}]}/><Content02 content={productLists}/>{/* <Swiper /> */}</>);};exportdefaultconnect(({ UAVEdgeModel })=>({
UAVEdgeModel,}))(UAVEdge);
办法五:办法四的加强版
思路:把数据处理干净传过去,使用useRef()处理数据
代码:
完整代码:
constGroundEdge=(props)=>{const{ GroundEdgeModel, dispatch }= props;const{ productLists, typeId, page }= GroundEdgeModel;const[data, setData]=useState(productLists);const myRef =useRef()useEffect(()=>{const myDeleteData = data.slice(0,1);
myRef.current = myDeleteData
},[data]);useEffect(()=>{dispatch({type:'GroundEdgeModel/getProductList',});},[]);return(<><Banner content={groDate}/><Content content={myRef.current}/><Content02 content={productLists}/>{/* <Swiper /> */}</>);};exportdefaultconnect(({ GroundEdgeModel })=>({
GroundEdgeModel,}))(GroundEdge);
办法六:办法五的加强版
期望后端接口数据:
实际后端接口数据:
代码:
完整代码:
constOnboardEdge=(props)=>{const{ OnboardEdgeModel, dispatch }= props;const{ productLists, typeId, page }= OnboardEdgeModel;const[info, setInfo]=useState(productLists);let infoRef =useRef();useEffect(()=>{if(productLists !==null){const myModityData = info.map((item)=>{return{...item,title: item.title.split('***'),};});
infoRef.current = myModityData[0];}},[info]);useEffect(()=>{dispatch({type:'OnboardEdgeModel/getProductList',});},[]);return(<div className={styles.home_box}><Banner content={onbDate}/>{/* <PageTitleThreeC content={productLists[0] || {}} /> */}<PageTitleThreeC content={infoRef.current}/><Content01 content={productLists[0]}/><Content02 content={productLists}/>{/* <Swiper /> */}</div>);};exportdefaultconnect(({ OnboardEdgeModel })=>({
OnboardEdgeModel,}))(OnboardEdge);
推荐使用:办法三useRef()
终极简单:不解决异步问题
之前解决了那么多,但useState在项目实际中依然没有解决。
终极简单思路:处理“干净的”数据传给组件,再传递的时候进行一个数组对象的截取react适用,vue没有实践过。
处理前:
处理后:
字符串转数组:
期待把后端的数据title字段进行一个split()截取
import React,{ useState, useEffect, useRef }from'react';import styles from'./index.less';exportdefaultfunctionindex({ content ={}}){let arr =[];if(content.title){let title = content.title;
arr = title.split('***');}return(<div className={styles.home_box}><div className={styles.title}>{arr[0]}</div><div className={styles.titleBottom}>{arr[1]}</div><p className={styles.des}>{content.des}</p></div>);}
总结:
大道求简。在写的过程中,一点一点完善,发现到了最后,有更加简单的方法,根本不用走那一套。
你要问我有没有意义,仁者见仁。
版权归原作者 影子信息 所有, 如有侵权,请联系我们删除。