前端 - React - - useEffect处理网络请求

问题还原:

在例如搜索场景下,用户输入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。