大家好!
我一直在开发一个轻量级的 React hook,我称之为 useAsync,它模仿 React Query 的一些基本功能(如获取、缓存、重试等),但以更紧凑的方式,易于定制的包。下面是其内部工作原理的快速分解,引用了相关的代码部分。如果您想查看完整代码,请前往存储库:
GitHub 上的完整源代码。
该钩子也可以在 npm 上作为 api-refetch.
虽然 React Query 和 SWR 都是很棒的库,但出于以下几个原因,我想要一种更实用的方法:
轻量化
虽然 React Query 和 SWR 功能丰富,但它们可能相对较大(React Query ~2.2 MB,SWR ~620 kB)。 api-refetch 大约 250 kB,使其非常适合较小的应用程序,其中包大小是一个大问题。该挂钩意味着作为另一个库(Intlayer)的依赖项安装。因此,解决方案的大小是一个重要的考虑因素。
易于定制和优化
我需要一些特定的功能,例如从本地存储存储/获取数据和使用简单的方法管理并行请求。
通过克隆存储库或将代码直接复制到您的项目中,您可以删除任何不需要的功能并仅保留您需要的功能。这不仅可以减少捆绑包大小,还可以最大限度地减少不必要的重新渲染和增加,为您提供根据您的特定要求量身定制的更精简、性能更高的解决方案。
无所需提供商
我想避免使用 Context Provider 来使钩子全局化,并使其使用尽可能简单。所以我根据 Zustand 商店制作了 hooke 的一个版本(参见下面的示例)。
学习练习
从头开始构建异步库是理解并发、缓存和状态管理内部结构的绝佳方法。
简而言之,滚动我自己的钩子是一个机会精确磨练我需要的功能(并跳过我不需要的功能),同时保持库小且易于理解。
React 钩子管理:
以下是 api-refetch 中的要点以及对 useAsync.tsx 中代码相关部分的简短引用。
// This map stores any in-progress Promise to avoid sending parallel requests // for the same resource across multiple components. const pendingPromises = new Map(); const fetch: T = async (...args) => { // Check if a request with the same key + args is already running if (pendingPromises.has(keyWithArgs)) { return pendingPromises.get(keyWithArgs); } // Otherwise, store a new Promise and execute const promise = (async () => { setQueryState(keyWithArgs, { isLoading: true }); // ...perform fetch here... })(); // Keep it in the map until it resolves or rejects pendingPromises.set(keyWithArgs, promise); return await promise; };
// Handle periodic revalidation if caching is enabled useEffect( () => { if (!revalidationEnabled || revalidateTime <= 0) return; // Revalidation is disabled if (!isEnabled || !enabled) return; // Hook is disabled if (isLoading) return; // Fetch is already in progress if (!isSuccess || !fetchedDateTime) return; // Should retry either of revalidate if (!(cacheEnabled || storeEnabled)) return; // Useless to revalidate if caching is disabled const timeout = setTimeout(() => { fetch(...storedArgsRef.current); }, revalidateTime); return () => clearTimeout(timeout); }, [ /* dependencies */ ] );
useEffect( () => { const isRetryEnabled = errorCount > 0 && retryLimit > 0; const isRetryLimitReached = errorCount > retryLimit; if (!isEnabled || !enabled) return; // Hook is disabled if (!isRetryEnabled) return; // Retry is disabled if (isRetryLimitReached) return; // Retry limit has been reached if (!(cacheEnabled || storeEnabled)) return; // Useless to retry if caching is disabled if (isLoading) return; // Fetch is already in progress if (isSuccess) return; // Hook has already fetched successfully const timeout = setTimeout(() => { fetch(...storedArgsRef.current); }, retryTime); return () => clearTimeout(timeout); }, [ /* dependencies */ ] );
// Auto-fetch data on hook mount if autoFetch is true useEffect( () => { if (!autoFetch) return; // Auto-fetch is disabled if (!isEnabled || !enabled) return; // Hook is disabled if (isFetched && !isInvalidated) return; // Hook have already fetched or invalidated if (isLoading) return; // Fetch is already in progress fetch(...storedArgsRef.current); }, [ /* dependencies */ ] );
查看完整代码,其中包括本地存储逻辑、查询失效等:
如果您有兴趣,请随意尝试、报告问题或做出贡献。非常感谢任何反馈!
复制代码或编码(repo)[https://github.com/aymericzip/api-refetch]
或者
// This map stores any in-progress Promise to avoid sending parallel requests // for the same resource across multiple components. const pendingPromises = new Map(); const fetch: T = async (...args) => { // Check if a request with the same key + args is already running if (pendingPromises.has(keyWithArgs)) { return pendingPromises.get(keyWithArgs); } // Otherwise, store a new Promise and execute const promise = (async () => { setQueryState(keyWithArgs, { isLoading: true }); // ...perform fetch here... })(); // Keep it in the map until it resolves or rejects pendingPromises.set(keyWithArgs, promise); return await promise; };
// Handle periodic revalidation if caching is enabled useEffect( () => { if (!revalidationEnabled || revalidateTime <= 0) return; // Revalidation is disabled if (!isEnabled || !enabled) return; // Hook is disabled if (isLoading) return; // Fetch is already in progress if (!isSuccess || !fetchedDateTime) return; // Should retry either of revalidate if (!(cacheEnabled || storeEnabled)) return; // Useless to revalidate if caching is disabled const timeout = setTimeout(() => { fetch(...storedArgsRef.current); }, revalidateTime); return () => clearTimeout(timeout); }, [ /* dependencies */ ] );
就是这样!尝试一下,然后让我知道进展如何。 GitHub 上非常欢迎提供反馈、问题或贡献。
GitHub: api-refetch
编码愉快!
以上是React 中用于异步数据获取和缓存的轻量级 Hook 展示的详细内容。更多信息请关注PHP中文网其他相关文章!