react 函數(shù)組件
常見的鉤子函數(shù):useState、useEffect
useState的異步
useState作為最常見的一個(gè)hook,在使用中總是會(huì)出現(xiàn)各種坑,最明顯的就是 useState 更新異步的問題。
- 問題描述:把接口返回的數(shù)據(jù),使用 useState 儲(chǔ)存起來,但是當(dāng)后面去改變這個(gè)數(shù)據(jù)的時(shí)候,每次拿到的都是上次的數(shù)據(jù),無法實(shí)時(shí)更新。或者我們在函數(shù)內(nèi)部使用 setState ,然后立即打印 state,打印的結(jié)果還是第一次的state 的值。
- 原因:useState 返回的更新對象的方法是異步的,要在下次重繪才能獲取新值,不要試圖在更改狀態(tài)之后立即獲取狀態(tài)。
- 控制臺(tái)報(bào)錯(cuò):
- 代碼,函數(shù)組件:
import React, { useState, useEffect } from 'react';
import styles from './index.less';
export default function index({ 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>
);
}
原因總結(jié):
這是因?yàn)镽eact里事件分為合成事件和原生事件,合成事件和鉤子函數(shù)都是異步的,原生事件是同步的。因?yàn)?strong>useState是異步事件,所以會(huì)出現(xiàn)不能及時(shí)獲取到最新值的情況。
useState 返回的更新狀態(tài)方法是異步的,要在下次重繪才能獲取新值。不要試圖在更改狀態(tài)之后立馬獲取狀態(tài)。
注意:useEffect的第二個(gè)參數(shù),我表明監(jiān)聽content.title但還是不行,因?yàn)樵赗eact的函數(shù)組件中使用useState進(jìn)行數(shù)據(jù)存儲(chǔ),導(dǎo)致數(shù)據(jù)異步,不能及時(shí)獲取當(dāng)前最新的數(shù)據(jù)。
解決辦法思路:
- 辦法一:先使用useRef進(jìn)行存儲(chǔ)數(shù)據(jù),再使用useEffect監(jiān)聽數(shù)據(jù)變化,并進(jìn)行更新。
在之后需要使用 info 數(shù)據(jù)的地方只需要獲取 infoRef.current 即可獲取最新的 info 數(shù)據(jù)內(nèi)容。
import React, { useState, useEffect, useRef } from 'react';
const Index = () => {
const [info, setInfo] = useState()
const infoRef = useRef()
useEffect(() => {
infoRef.current = info
}, [info])
}
- 辦法二:使用回調(diào)函數(shù)更改數(shù)據(jù)
- useState 使用的兩種方式
我們知道,useState中的 [ ] 是一個(gè)解構(gòu)運(yùn)算,第一個(gè)是設(shè)置的值,第二個(gè)是用來改變 state 的方法。
一般情況下,我們使用第一種方式即可,但在某些特殊情況下,第一種方式獲取到的值不是最新設(shè)置的。
const [data, setData] = useState(1)
setData(data + 1)
const [data, setData] = useState(0)
setData((prev) => prev + 1); // prev 是data 改變之前的值,return 返回的值會(huì)作為新狀態(tài)覆蓋data
原文鏈接:React hooks中 useState踩坑——異步問題
實(shí)際解決辦法:
辦法一:頁面能渲染出來,但控制臺(tái)報(bào)錯(cuò),監(jiān)聽會(huì)一直存在,很耗能
控制臺(tái)報(bào)錯(cuò):
中文解釋:警告:超過了最大更新深度。當(dāng)組件在useEffect內(nèi)部調(diào)用setState時(shí),可能會(huì)發(fā)生這種情況,但useEffect要么沒有依賴關(guān)系數(shù)組,要么依賴關(guān)系在每次渲染時(shí)都會(huì)發(fā)生變化。
代碼:
完整代碼:
const UAVEdge = (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 /> */}
</>
);
};
export default connect(({ UAVEdgeModel }) => ({
UAVEdgeModel,
}))(UAVEdge);
辦法二:useState(使用回調(diào)函數(shù))
控制臺(tái)報(bào)錯(cuò):
中文解釋:
未捕獲錯(cuò)誤:重新渲染過多。React限制渲染次數(shù),以防止無限循環(huán)。
代碼:
完整代碼:
import React, { useState, useEffect } from 'react';
import styles from './index.less';
export default function index({ 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>
);
}
辦法三:辦法一的改進(jìn)
代碼:
完整代碼:
import React, { useState, useEffect, useRef } from 'react';
import styles from './index.less';
export default function index({ 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>
);
}
辦法四:組件傳參的時(shí)候,傳數(shù)據(jù)
思路:組件傳參的時(shí)候,傳數(shù)據(jù)
控制臺(tái)報(bào)錯(cuò):
中文解釋:
需要獨(dú)一無二的key??墒俏颐髅饕呀?jīng)給了,為什么?
接口返回的數(shù)據(jù):
函數(shù)組件傳參,代碼:
函數(shù)組件傳參,完整代碼:
const UAVEdge = (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 /> */}
</>
);
};
export default connect(({ UAVEdgeModel }) => ({
UAVEdgeModel,
}))(UAVEdge);
辦法五:辦法四的加強(qiáng)版
思路:把數(shù)據(jù)處理干凈傳過去,使用useRef()處理數(shù)據(jù)
代碼:
完整代碼:
const GroundEdge = (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 /> */}
</>
);
};
export default connect(({ GroundEdgeModel }) => ({
GroundEdgeModel,
}))(GroundEdge);
辦法六:辦法五的加強(qiáng)版
期望后端接口數(shù)據(jù):
實(shí)際后端接口數(shù)據(jù):
代碼:
完整代碼:
const OnboardEdge = (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>
);
};
export default connect(({ OnboardEdgeModel }) => ({
OnboardEdgeModel,
}))(OnboardEdge);
推薦使用:辦法三useRef()
終極簡單:不解決異步問題
之前解決了那么多,但useState在項(xiàng)目實(shí)際中依然沒有解決。
終極簡單思路:處理“干凈的”數(shù)據(jù)傳給組件,再傳遞的時(shí)候進(jìn)行一個(gè)數(shù)組對象的截取react適用,vue沒有實(shí)踐過。
處理前:
處理后:
字符串轉(zhuǎn)數(shù)組:
期待把后端的數(shù)據(jù)title字段進(jìn)行一個(gè)split()截取文章來源:http://www.zghlxwxcb.cn/news/detail-769733.html
import React, { useState, useEffect, useRef } from 'react';
import styles from './index.less';
export default function index({ 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>
);
}
總結(jié):
大道求簡。在寫的過程中,一點(diǎn)一點(diǎn)完善,發(fā)現(xiàn)到了最后,有更加簡單的方法,根本不用走那一套。
你要問我有沒有意義,仁者見仁。文章來源地址http://www.zghlxwxcb.cn/news/detail-769733.html
到了這里,關(guān)于02react 函數(shù)組件useState的異步問題的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!