自定义useAsyncEffect依赖溢出:如何实现单次运行+依赖更新触发
问题分析
你开发的useAsyncEffect Hook在依赖项为空数组时出现浏览器溢出,核心原因是Hook内部未正确处理异步操作的清理逻辑,或是状态更新触发了无限重渲染循环。当依赖为空数组时,预期仅执行一次,但错误的状态更新逻辑会导致组件反复渲染,最终引发溢出。
修复方案
要实现「仅运行一次,依赖更新时重新运行」的目标,需确保Hook满足:异步操作的清理、依赖变化时重新执行、避免无限重渲染。以下是修正后的useAsyncEffect实现:
import { useEffect, useRef, useState } from 'react'; const useAsyncEffect = <T>( effect: () => Promise<T | undefined>, deps: React.DependencyList ) => { const [data, setData] = useState<T | undefined>(); const [error, setError] = useState<Error | undefined>(); const [loading, setLoading] = useState(true); const isMounted = useRef(true); useEffect(() => { isMounted.current = true; const execute = async () => { try { setLoading(true); const result = await effect(); if (isMounted.current) { setData(result); setError(undefined); } } catch (err) { if (isMounted.current) { setError(err as Error); setData(undefined); } } finally { if (isMounted.current) { setLoading(false); } } }; execute(); return () => { isMounted.current = false; }; }, deps); return { data, error, loading }; }; export default useAsyncEffect;
关键改进点
- 挂载状态追踪:用
useRef的isMounted标记组件是否仍处于挂载状态,避免异步操作完成后更新已卸载组件的状态,从根源上减少无效重渲染。 - 受控状态更新:仅在组件挂载时更新
data、error、loading状态,防止无效状态更新触发循环渲染。 - 依赖正确绑定:将传入的
deps直接绑定到内部useEffect,确保依赖变化时重新执行异步逻辑;当deps为空数组时,仅在组件挂载时执行一次。
使用验证
用你提供的示例代码测试:
import useAsyncEffect from "./useAsyncEffect"; const useSomethingExample = ({ processes, datasets }) => { console.log(processes); console.log(datasets); return useAsyncEffect(async () => { await new Promise((resolve) => { setTimeout(() => { resolve({ processes, datasets }); }, 300); }); }, [processes, datasets]); }; export default useSomethingExample;
当processes和datasets为空数组时,Hook仅执行一次,不会引发浏览器溢出;当这两个依赖项更新时,Hook会自动重新执行异步逻辑。
内容的提问来源于stack exchange,提问作者Raul H
相关产品推荐
相关产品推荐

