React useEffect不支持async function示例分析

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

引言

useEffect相比大家都耳熟能详啦,如下这种写法,应该是非常常见的需求。

useEffect(async () => {
   await getPoiInfo(); // 请求数据
}, []);

但是 React 本身并不支持这么做,理由是 effect function 应该返回一个销毁函数(effect:是指return返回的cleanup函数),如果 useEffect 第一个参数传入 async,返回值则变成了 Promise,会导致 react 在调用销毁函数的时候报错 :function.apply is undefined。

React为什么这么设计呢?

1、useEffect 的返回值是要在卸载组件时调用的,React 需要在 mount 的时候马上拿到这个值,不然就乱套了

2、useEffect() 可能有个潜在逻辑:第二次触发 useEffect 里的回调前,前一次触发的行为都执行完成,返回的清理函数也执行完成。这样逻辑才清楚。而如果是异步的,情况会变得很复杂,可能会很容易写出有 bug 的代码。

下面有两种改进的方法大家可以参考下:

简单改造

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

第一种 在内部创建一个异步函数anyNameFunction,等待他的结果,然后调用setData

但是这种方式存在一个问题,如果asyncFunction请求有依赖外部的参数,如果不更新requestData 的 effect 的依赖,effect 就不会同步 props 和 state 带来的变更,也就不回重新请求数据

useEffect(() => {
    // Create an scoped async function in the hook
    // 注意如果函数没有使用组件内的任何值,可以把它提到组件外面去定义
    // 下面代码可以提到外面,可以自由地在 effect 中使用,下面就不改啦
    async function asyncFunction() {
      await requestData();
      setData(data)
    }
    // Execute the created function directly
    anyNameFunction();
 }, []); // 这里设置成[]数组,因为我们只想在挂载的时候运行它一次

或者 useEffect中异步函数采用IIFE写法( Immediately Invoked Function Expression即立即调用的函数式表达式)

useEffect(() => {
  // Using an IIFE
  (async function anyNameFunction() {
    await requestData();
  })();
}, []);

2、把异步提取成单独函数或自定义hook(推荐)

第一种自定义 hook包裹,然后再effect中通过promise.then调用(github上大佬给的答案:github

// 自定义hook
function useAsyncEffect(effect: () => Promise<void | (() => void)>, dependencies?: any[]) {
  return useEffect(() => {
    const cleanupPromise = effect()
    return () => { cleanupPromise.then(cleanup => cleanup && cleanup()) }
  }, dependencies)
}
// 使用
useAsyncEffect(async () => {
    const count = await fetchData()
    setCount(count)
  }, [fetchData])

或者利用useCallback 包装成hook

useCallback 本质上是添加了一层依赖检查,使用useCallback,函数完全可以参与到数据流中,可以说如果一个函数的输入改变了,这个函数就改变了,如果没有,函数也不会改变。

下面的例子中会依赖 type ,如果 type 保持不变,requestData 也会保持不变,effect 也不会重新运行,但是如果 type 修改了,requestData 也会随之改变,因此会重新请求数据。

// 封装
const requestData = useCallback(async () => {
  changeLoading(true);
  changeError(false);
  changeList([]);
  requestAPI.getFeature({ type }).then((data) => {
    if (data) {
      changeList(data);
    }
  }).catch((e) => {
    changeError(true);
  }).finally(() => {
    changeLoading(false);
  });
}, [type]); // type改变会重新生成函数
// 普通接口请求
useEffect(() => {
  requestData();
}, [requestData]);
// 单独处理外层刷新的接口请求
// refreshing是props传递的过来的,不应该与state状态改变混在一起,这也是hook的优势,将不相关的状态逻辑拆分成更细粒度
useEffect(() => {
  if (!refreshing) {
    return;
  }
  requestData().then(() => {
    getRefreshStatus(false);
  });
}, [refreshing]);

关于为什么不支持异步的原理可以看下这篇文章里通过源码的分析:useEffect 中为啥不能使用 async

有任何疑问欢迎评论沟通,我会继续更新!

其他相关文档:

https://heptaluan.github.io/2020/11/07/React/17/

https://www.robinwieruch.de/react-h

以上就是React useEffect不支持async function示例分析的详细内容,更多关于useEffect不支持async function的资料请关注我们其它相关文章!

(0)

相关推荐

  • useEffect支持async及await使用方式

    目录 引言 背景 React 为什么要这么做? useEffect 怎么支持 async...await... 自定义 hooks 还可以支持 useEffect 的清除机制么? 总结与思考 引言 本文是深入浅出 ahooks 源码系列文章的第六篇,这个系列的目标主要有以下几点: 加深对 React hooks 的理解. 学习如何抽象自定义 hooks.构建属于自己的 React hooks 工具库. 培养阅读学习源码的习惯,工具库是一个对源码阅读不错的选择. 注:本系列对 ahooks 的源码

  • 浅谈react useEffect闭包的坑

    问题代码 看一段因为useEffect导致的闭包问题代码 const btn = useRef(); const [v, setV] = useState(''); useEffect(() => { let clickHandle = () => { console.log('v:', v); } btn.current.addEventListener('click', clickHandle) return () => { btn.removeEventListener('clic

  • React useEffect的理解与使用

    React16.8新增的useEffec这个hook函数就是处理副作用的. 所谓的"副作用",举个通俗一点的例子,假如感冒了本来吃点药就没事了,但是吃了药发现身体过敏了,而这个"过敏"就是副作用. 放到React中,本来只是想渲染DOM展示到页面上,但除了DOM之外还有数据,而这些数据必须从外部的数据源中获取,这个"获取外部数据源"的过程就是副作用. useEffect怎么用可以参考官网给出的例子,这里主要针对使用useEffect过程中遇到的问

  • React中useEffect 与 useLayoutEffect的区别

    目录 前置知识 useEffect commitBeforeMutationEffects commitMutationEffects commitLayoutEffects 后续阶段 useLayoutEffect 结论 前置知识 我们可以将 React 的工作流程划分为几大块: render 阶段:主要生成 Fiber节点 并构建出完整的 Fiber树 commit 阶段:在上一个render 阶段中会在 rootFiber 上生成一条副作用链表,应用的DOM操作就会在本阶段执行 commi

  • React useEffect不支持async function示例分析

    目录 引言 React为什么这么设计呢? 简单改造 1.简单改造的写法(不推荐) 2.把异步提取成单独函数或自定义hook(推荐) 引言 useEffect相比大家都耳熟能详啦,如下这种写法,应该是非常常见的需求. useEffect(async () => { await getPoiInfo(); // 请求数据 }, []); 但是 React 本身并不支持这么做,理由是 effect function 应该返回一个销毁函数(effect:是指return返回的cleanup函数),如果

  • React 状态管理工具优劣势示例分析

    目录 什么是状态管理? React 状态管理方案 方案介绍 方案对比 Source 相关建议 什么是状态管理? “状态”是描述应用程序当前行为的任何数据.这可能包括诸如“从服务器获取的对象列表”.“当前选择的项目”.“当前登录用户的名称”和“此模式是否打开?”等值. 众所周知,我们在研发一个复杂应用的过程中,一套好的状态管理方案是必不可少的,既能提升研发效率,又能降低研发维护成本,那么状态管理方案那么多,它们有什么不同,我们又该如何选择适合当前应用的方案呢? 本期将主要就 react 的常用状态

  • React Hooks钩子中API的使用示例分析

    目录 hooks是什么 Hooks的作用 使用Hooks组件前后开发模式的对比 Hooks使用策略 为什么要有Hooks useState useEffect使用 useEffect依赖项 使用情景 useMemo使用 useMemo缓存组件方式 useMemo和useEffect的区别 useCallback使用 useContext使用 useRef使用 为什么在函数组件中无法使用ref 如何在类组件中使用ref属性 自定义hooks hooks是什么 hooks理解字面意思就是钩子,是一些

  • React Hook 'useEffect' is called in function报错解决

    目录 总览 声明组件 声明自定义钩子 总结 总览 为了解决错误"React Hook 'useEffect' is called in function that is neither a React function component nor a custom React Hook function",可以将函数名的第一个字母大写,或者使用use作为函数名的前缀.比如说,useCounter使其成为一个组件或一个自定义钩子. 这里有个示例用来展示错误是如何发生的. // App.j

  • React Context原理深入理解源码示例分析

    目录 正文 一.概念 二.使用 2.1.React.createContext 2.2.Context.Provider 2.3.React.useContext 2.4.Example 三.原理分析 3.1.createContext 函数实现 3.2. JSX 编译 3.3.消费组件 - useContext 函数实现 3.4.Context.Provider 在 Fiber 架构下的实现机制 3.5.小结 四.注意事项 五.对比 useSelector 正文 在 React 中提供了一种「

  • 如何解决React useEffect钩子带来的无限循环问题

    目录 什么导致的无限循环以及如何解决它们 如何解决这个问题 使用函数作为依赖项 使用数组作为依赖项 将对象作为依赖项传递 传递不正确的依赖项 结尾 React的useEffect Hook可以让用户处理应用程序的副作用.例如: 从网络获取数据:应用程序通常在第一次加载时获取并填充数据.这可以通过useEffect函数实现 操作UI:应用程序应该响应按钮点击事件(例如,打开一个菜单) 设置或结束计时器:如果某个变量达到预定义值,则内置计时器应自行停止或启动 尽管useEffect Hook在Rea

  • React useEffect异步操作常见问题小结

    目录 三个常见的问题: 一.react hooks发异步请求 二.如何在组件加载的时候发起异步任务 三.如果在响应回来之前组件被销毁了会怎样? 四.如何在组件交互时发起异步任务 为什么两种写法会有差异呢? 五.其他陷阱 总结 useEffect 和异步任务搭配使用的时候会遇到的一些坑总结. 三个常见的问题: 1.如何在组件加载的时候发起异步任务 2.如何在组件交互的时候发起异步任务 3.其他陷阱 一.react hooks发异步请求 1.使用useEffect发起异步任务,第二个参数使用空数组可

  • react实现浏览器自动刷新的示例代码

    在单页应用如此流行的今天,曾经令人惊叹的前端路由已经成为各大框架的基础标配,每个框架都提供了强大的路由功能,导致路由实现变的复杂.想要搞懂路由内部实现还是有些困难的,但是如果只想了解路由实现基本原理还是比较简单的.本文针对前端路由主流的实现方式 hash 和 history,提供了原生JS/React/Vue 共计六个版本供参考,每个版本的实现代码约 25~40 行左右(含空行). 什么是前端路由? 路由的概念来源于服务端,在服务端中路由描述的是 URL 与处理函数之间的映射关系. 在 Web

  • React前端DOM常见Hook封装示例上

    目录 引言 useEventListener useClickAway useEventTarget useTitle useFavicon 引言 本文是深入浅出 ahooks 源码系列文章的第十四篇,这个系列的目标主要有以下几点: 加深对 React hooks 的理解. 学习如何抽象自定义 hooks.构建属于自己的 React hooks 工具库. 培养阅读学习源码的习惯,工具库是一个对源码阅读不错的选择. 上一篇我们探讨了 ahooks 对 DOM 类 Hooks 使用规范,以及源码中是

随机推荐