[前端] ReactuseEffect不支持asyncfunction示例分析

1947 0
王子 2022-10-21 15:29:01 | 显示全部楼层 |阅读模式
目录

    引言React为什么这么设计呢?简单改造
      1、简单改造的写法(不推荐)2、把异步提取成单独函数或自定义hook(推荐)



引言

useEffect相比大家都耳熟能详啦,如下这种写法,应该是非常常见的需求。
  1. useEffect(async () => {
  2.    await getPoiInfo(); // 请求数据
  3. }, []);
复制代码
但是 React 本身并不支持这么做,理由是 effect function 应该返回一个销毁函数(effect:是指return返回的cleanup函数),如果 useEffect 第一个参数传入 async,返回值则变成了 Promise,会导致 react 在调用销毁函数的时候报错 :function.apply is undefined。

React为什么这么设计呢?

1、useEffect 的返回值是要在卸载组件时调用的,React 需要在 mount 的时候马上拿到这个值,不然就乱套了
2、useEffect() 可能有个潜在逻辑:第二次触发 useEffect 里的回调前,前一次触发的行为都执行完成,返回的清理函数也执行完成。这样逻辑才清楚。而如果是异步的,情况会变得很复杂,可能会很容易写出有 bug 的代码。
下面有两种改进的方法大家可以参考下:
</p>简单改造


1、简单改造的写法(不推荐)

第一种 在内部创建一个异步函数anyNameFunction,等待他的结果,然后调用setData
但是这种方式存在一个问题,如果asyncFunction请求有依赖外部的参数,如果不更新requestData 的 effect 的依赖,effect 就不会同步 props 和 state 带来的变更,也就不回重新请求数据
  1. useEffect(() => {
  2.     // Create an scoped async function in the hook
  3.     // 注意如果函数没有使用组件内的任何值,可以把它提到组件外面去定义
  4.     // 下面代码可以提到外面,可以自由地在 effect 中使用,下面就不改啦
  5.     async function asyncFunction() {
  6.       await requestData();
  7.       setData(data)
  8.     }
  9.     // Execute the created function directly
  10.     anyNameFunction();
  11. }, []); // 这里设置成[]数组,因为我们只想在挂载的时候运行它一次
复制代码
或者 useEffect中异步函数采用IIFE写法( Immediately Invoked Function Expression即立即调用的函数式表达式)
  1. useEffect(() => {
  2.   // Using an IIFE
  3.   (async function anyNameFunction() {
  4.     await requestData();
  5.   })();
  6. }, []);
复制代码
2、把异步提取成单独函数或自定义hook(推荐)

第一种自定义 hook包裹,然后再effect中通过promise.then调用(github上大佬给的答案:github)
  1. // 自定义hook
  2. function useAsyncEffect(effect: () => Promise<void | (() => void)>, dependencies?: any[]) {
  3.   return useEffect(() => {
  4.     const cleanupPromise = effect()
  5.     return () => { cleanupPromise.then(cleanup => cleanup && cleanup()) }
  6.   }, dependencies)
  7. }
  8. // 使用
  9. useAsyncEffect(async () => {
  10.     const count = await fetchData()
  11.     setCount(count)
  12.   }, [fetchData])
复制代码
或者利用useCallback 包装成hook
useCallback 本质上是添加了一层依赖检查,使用useCallback,函数完全可以参与到数据流中,可以说如果一个函数的输入改变了,这个函数就改变了,如果没有,函数也不会改变。
下面的例子中会依赖 type ,如果 type 保持不变,requestData 也会保持不变,effect 也不会重新运行,但是如果 type 修改了,requestData 也会随之改变,因此会重新请求数据。
  1. // 封装
  2. const requestData = useCallback(async () => {
  3.   changeLoading(true);
  4.   changeError(false);
  5.   changeList([]);
  6.   requestAPI.getFeature({ type }).then((data) => {
  7.     if (data) {
  8.       changeList(data);
  9.     }
  10.   }).catch((e) => {
  11.     changeError(true);
  12.   }).finally(() => {
  13.     changeLoading(false);
  14.   });
  15. }, [type]); // type改变会重新生成函数
  16. // 普通接口请求
  17. useEffect(() => {
  18.   requestData();
  19. }, [requestData]);
  20. // 单独处理外层刷新的接口请求
  21. // refreshing是props传递的过来的,不应该与state状态改变混在一起,这也是hook的优势,将不相关的状态逻辑拆分成更细粒度
  22. useEffect(() => {
  23.   if (!refreshing) {
  24.     return;
  25.   }
  26.   requestData().then(() => {
  27.     getRefreshStatus(false);
  28.   });
  29. }, [refreshing]);
复制代码
关于为什么不支持异步的原理可以看下这篇文章里通过源码的分析:useEffect 中为啥不能使用 async
有任何疑问欢迎评论沟通,我会继续更新!
其他相关文档:
https://heptaluan.github.io/2020/11/07/React/17/
https://www.robinwieruch.de/react-hooks-fetch-data/
以上就是React useEffect不支持async function示例分析的详细内容,更多关于useEffect不支持async function的资料请关注中国红客联盟其它相关文章!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

中国红客联盟公众号

联系站长QQ:5520533

admin@chnhonker.com
Copyright © 2001-2025 Discuz Team. Powered by Discuz! X3.5 ( 粤ICP备13060014号 )|天天打卡 本站已运行