From d73b31c2268f74cf82d3653239ceea0b702ba873 Mon Sep 17 00:00:00 2001 From: chengpeiquan Date: Thu, 29 May 2025 00:21:50 +0800 Subject: [PATCH] fix(docs): stabilize useRefreshKey deps with memoized string conversion --- docs/src/content/docs/guides/faq.mdx | 24 +++++++++++++++++++----- docs/src/content/docs/zh/guides/faq.mdx | 24 +++++++++++++++++++----- docs/src/hooks/use-refresh-key.ts | 18 ++++++++++++++++-- 3 files changed, 54 insertions(+), 12 deletions(-) diff --git a/docs/src/content/docs/guides/faq.mdx b/docs/src/content/docs/guides/faq.mdx index 2541678..d8bc9d6 100644 --- a/docs/src/content/docs/guides/faq.mdx +++ b/docs/src/content/docs/guides/faq.mdx @@ -48,16 +48,30 @@ If there is a State dependency that can trigger React.useEffect, you can also im ```ts // e.g. src/hooks/use-refresh-key.ts -import React from 'react' +import { useEffect, useMemo, useState } from 'react' export const useRefreshKey = (deps: unknown[]) => { - const [refreshKey, setRefreshKey] = React.useState(Date.now()) - - React.useEffect(() => { + const [refreshKey, setRefreshKey] = useState(Date.now()) + + /** + * Ensure dependencies are stable between renders. If deps is an array, + * convert it to a string to prevent infinite loops. + * + * ⚠️ Note: `Array.prototype.toString()` is a shallow and order-sensitive + * representation. It may not behave as expected for objects or nested arrays. + * For more accurate results, consider using JSON.stringify() or a deep + * comparison function like lodash.isEqual(). + */ + const effectDeps = useMemo( + () => (Array.isArray(deps) ? deps.toString() : deps), + [deps], + ) + + useEffect(() => { requestAnimationFrame(() => { setRefreshKey(Date.now()) }) - }, [deps]) + }, [effectDeps]) return { refreshKey, diff --git a/docs/src/content/docs/zh/guides/faq.mdx b/docs/src/content/docs/zh/guides/faq.mdx index ccad361..49711fd 100644 --- a/docs/src/content/docs/zh/guides/faq.mdx +++ b/docs/src/content/docs/zh/guides/faq.mdx @@ -48,16 +48,30 @@ const onResize = () => setRefreshKey(Date.now()) ```ts // e.g. src/hooks/use-refresh-key.ts -import React from 'react' +import { useEffect, useMemo, useState } from 'react' export const useRefreshKey = (deps: unknown[]) => { - const [refreshKey, setRefreshKey] = React.useState(Date.now()) - - React.useEffect(() => { + const [refreshKey, setRefreshKey] = useState(Date.now()) + + /** + * 确保依赖项在渲染之间保持稳定。如果 deps 是数组, + * 将其转换为字符串以防止进入无限循环。 + * + * ⚠️ 注意:`Array.prototype.toString()` 是浅层的且对顺序敏感, + * 对于对象或嵌套数组,可能不会按预期工作。 + * 若需要更准确的判断,可考虑使用 JSON.stringify(), + * 或像 lodash.isEqual() 这样的深度比较函数。 + */ + const effectDeps = useMemo( + () => (Array.isArray(deps) ? deps.toString() : deps), + [deps], + ) + + useEffect(() => { requestAnimationFrame(() => { setRefreshKey(Date.now()) }) - }, [deps]) + }, [effectDeps]) return { refreshKey, diff --git a/docs/src/hooks/use-refresh-key.ts b/docs/src/hooks/use-refresh-key.ts index 5edd466..5bd3c20 100644 --- a/docs/src/hooks/use-refresh-key.ts +++ b/docs/src/hooks/use-refresh-key.ts @@ -1,13 +1,27 @@ -import { useEffect, useState } from 'react' +import { useEffect, useMemo, useState } from 'react' export const useRefreshKey = (deps: unknown[]) => { const [refreshKey, setRefreshKey] = useState(Date.now()) + /** + * Ensure dependencies are stable between renders. If deps is an array, + * convert it to a string to prevent infinite loops. + * + * ⚠️ Note: `Array.prototype.toString()` is a shallow and order-sensitive + * representation. It may not behave as expected for objects or nested arrays. + * For more accurate results, consider using JSON.stringify() or a deep + * comparison function like lodash.isEqual(). + */ + const effectDeps = useMemo( + () => (Array.isArray(deps) ? deps.toString() : deps), + [deps], + ) + useEffect(() => { requestAnimationFrame(() => { setRefreshKey(Date.now()) }) - }, [deps]) + }, [effectDeps]) return { refreshKey,