问题还原:
在例如搜索场景下,用户输入a,发起网络请求"a",紧接着输入b,又发起网络请求"ab",此时因网络问题先返回了"ab"的结果,再返回"a"的结果,就导致输入ab,展示的是a的结果。
方案一 async/await(错误):
javascript
const [list,setList] = useState([...]);
useEffect(async ()=>{
const res = await getList(id)
setList(res)
},[id])
useEffect设计规则:要么不返回任何东西,要么返回清理函数。async隐式返回Promise,所以违反useEffect规则。
方案二 开关控制/防抖(可行):
javascript
const [list,setList] = useState([...]);
useEffect(()=>{
let isFlag = true;
getList(id).then((res)=>{
if(ifFlag){
setList(res)
}
});
return ()=>{isFlag = false}
},[id])
当getList网络请求还未响应时,依赖项id改变了,则react会先执行完第一次的useEffect,将第一个isFlag改为false,终止setList,然后第二次执行useEffect。
缺点1:如果不执行setList,则此次网络请求就是多余的,会浪费网络资源。
缺点2:如果组件因为非依赖项问题而切换,比如在网络请求中被销毁,则会导致setList依然执行。
方案三 AbortController(推荐):
javascript
const [list,useList] = useState([...]);
useEffect(() => {
const controller = new AbortController();
getList(id, { signal: controller.signal })
.then((res) => {
setList(res);
})
.catch((error) => {
if (error.name !== 'AbortError') {
// 处理主动终止的错误
}
});
return () => {
controller.abort(); // 取消正在进行的请求
};
}, [id]);
AbortController对象(浏览器内置api)生成一个唯一值(controller.signal),当网络请求中,执行了清除函数,那么signal则会发生改变,请求就会进入catch。