尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

react-use 中的 useAsync:用 Hooks 优雅处理异步函数与 Promise 状态

react-use 中的 useAsync:用 Hooks 优雅处理异步函数与 Promise 状态 react-use 中的 useAsync用 Hooks 优雅处理异步函数与 Promise 状态【免费下载链接】react-useReact Hooks — 项目地址: https://gitcode.com/gh_mirrors/re/react-use导读useAsync是 react-use 提供的一个副作用类Side effectsHook用于在 React 函数组件中解析一个async函数或任何返回 Promise 的函数并自动管理loading、error、value三种异步状态。它把发起请求 → 等待加载 → 成功取值 / 失败取错这一整套异步生命周期封装为声明式状态让你在组件里只需关注渲染逻辑而不用手写useStateuseEffect的样板代码。读完本文你将掌握useAsync的完整 API、源码内部的执行原理、依赖变更时的重新求值机制以及它与useAsyncFn、useAsyncRetry的搭配用法。核心用法让异步结果成为组件状态useAsync接收一个返回 Promise 的函数并在函数执行期间自动更新返回的状态对象。官方文档给出的典型用法如下import {useAsync} from react-use; const Demo ({url}) { const state useAsync(async () { const response await fetch(url); const result await response.text(); return result }, [url]); return ( div {state.loading ? divLoading.../div : state.error ? divError: {state.error.message}/div : divValue: {state.value}/div } /div ); };从 useAsync 的官方文档 可以看出这个 Hook 的核心价值在于你只需要提供做什么async 函数和何时重做依赖数组剩下的状态管理全部交给 Hook 完成。在上面的例子中当url变化时useAsync会自动重新发起fetch请求并把最新结果同步到state中供渲染层消费。API 签名与返回结构文档中的参考签名非常简洁useAsync(fn, args?: any[]);结合源码 src/useAsync.ts 可以得出更精确的类型定义useAsyncT extends FunctionReturningPromise( fn: T, deps: DependencyList [] )其中fn一个async函数或返回 Promise 的函数类型为FunctionReturningPromise即(...args: any[]) Promiseany定义见 src/misc/types.ts。deps依赖数组DependencyList默认值为[]。当依赖变化时Hook 会重新执行fn。返回值是一个AsyncStateT状态对象其类型定义在 src/useAsyncFn.ts 中属于可辨识联合discriminated union按loading标志划分出四种互斥形态状态形态loadingerrorvalue初始/加载中true可为Error尚未完成可为旧值失败falseErrorundefined成功falseundefinedTPromise 解析值由于PromiseType见 src/misc/types.ts会从 Promise 中推导出解析值类型因此state.value具备完整的类型提示。在 JSX 中你可以直接通过state.loading/state.error/state.value三个字段分支渲染文档示例中的三元嵌套正是这一联合类型在渲染层的典型应用。源码原理useAsync 是如何工作的useAsync本身实现非常精简只有约 20 行它本质上是useAsyncFn的一层自动执行封装。完整实现如下src/useAsync.tsexport default function useAsyncT extends FunctionReturningPromise( fn: T, deps: DependencyList [] ) { const [state, callback] useAsyncFn(fn, deps, { loading: true, }); useEffect(() { callback(); }, [callback]); return state; }其中有两个关键设计初始状态为loading: true它把initialState设置为{ loading: true }这意味着首次挂载时组件就处于加载态符合请求已发起、结果未返回的真实语义。这也是测试中initially starts loading断言成立的原因见 tests/useAsync.test.tsx。通过useEffect自动触发挂载后立即调用callback()执行异步函数并把callback作为依赖。由于callback由useCallback(fn, deps)创建见 src/useAsyncFn.ts当deps变化时callback引用更新useEffect会重新执行从而自动重新发起异步任务。底层状态机useAsyncFn 的执行细节useAsync的所有异步逻辑都委托给useAsyncFnsrc/useAsyncFn.ts其核心机制包括并发安全callId 守卫内部用lastCallId引用计数每次调用callback时callId自增Promise 完成回调里只有callId lastCallId.current的最新一次调用才允许写入状态src/useAsyncFn.ts。这保证了快速连续触发时过期请求的结果不会覆盖新请求防止竞态条件。卸载安全isMounted 守卫借助useMountedStatesrc/useMountedState.ts跟踪组件挂载状态组件卸载后异步回调不会再去setState避免 React 的对已卸载组件更新状态警告。成功与失败分流.then的成功回调写入{ value, loading: false }失败回调写入{ error, loading: false }。注意失败分支把error原样存入状态因此state.error可能是 Error 实例也可能是任意 rejected 值测试中 reject 了一个字符串yay见 tests/useAsync.test.tsx。这些实现细节共同保证了即使在高频依赖变更、组件提前卸载、异步任务交错等场景下useAsync返回的状态始终与当前最新一次请求保持一致。依赖变更与重新求值useAsync的第二个参数deps是重新执行的触发器。官方文档的[url]用法已经体现了URL 变化 → 重新请求的语义。从测试用例可以更清楚地看到两种触发方式tests/useAsync.test.tsxfn 本身作为依赖传入新的函数引用会触发重新执行且callCount会递增证明新请求确实被发起。额外依赖变化测试中通过useCallback(() fn(counter), [counter])把外部状态counter织入回调counter从 0 变为 1 后useAsync自动用新参数重新执行并返回counter is 1 and callCount is 2。因此在实际项目中凡是异步函数内部读取的外部值都应列入deps数组同时建议像测试那样结合useCallback/useMemo稳定函数引用避免因函数每次渲染都变化而导致不必要的重复请求。搭配使用useAsyncFn 与 useAsyncRetry围绕useAsyncreact-use 还提供了两个高度关联的 Hook适合不同的场景均从 src/index.ts 统一导出useAsyncFnuseAsync的手动版。它返回[state, callback]元组异步执行完全由你掌控例如绑定到表单提交、按钮点击事件且支持向fn传递任意参数。useAsync正是useAsyncFn(fn, deps, { loading: true })加上自动调用的组合。useAsyncRetry在useAsync之上增加retry()方法见 src/useAsyncRetry.ts通过递增attempt计数并把其并入依赖数组来触发重新执行非常适合加载失败后点击重试的场景。其官方文档示例docs/useAsyncRetry.md展示了!loading button onClick{() state.retry()}Start loading/button的用法实现中还在开发环境下对加载中调用 retry给出 no-op 提示避免重复触发。实践建议与注意事项渲染分支要覆盖三种状态loading、error、value是互斥且完备的官方示例的三元嵌套或 Storybook 示例见 stories/useAsync.story.tsx都值得直接借鉴必要时可以像 Story 那样用JSON.stringify(state)调试完整状态。泛型参数可显式指定如useAsyncstring(...)可以约束state.value的类型让state.error.message、state.value的访问都获得类型保护。注意依赖稳定性fn若在渲染期间每次重建请用useCallback包裹或将其作为依赖传入以控制请求频率。失败时 error 的类型state.error的类型是Error | undefined但实际运行时 rejected 值可能不是 Error 实例如测试中的字符串生产代码建议对error做容错处理。总结useAsync用不到 20 行源码把执行 Promise 管理三态 依赖重跑 防竞态 防泄漏这一整套异步状态管理收拢为声明式 API是 react-use 中副作用类 Hook 的典型代表。掌握它之后再遇到useAsyncFn手动触发与useAsyncRetry带重试时就能理解它们只是自动执行与重试增强两个方向上的变体。无论是数据拉取、接口轮询还是任何 Promise 驱动的场景useAsync都能让组件代码更简洁、更可控。【免费下载链接】react-useReact Hooks — 项目地址: https://gitcode.com/gh_mirrors/re/react-use创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表