diff --git a/packages/core/changelog.md b/packages/core/changelog.md index 989e8953..3c2b8b04 100644 --- a/packages/core/changelog.md +++ b/packages/core/changelog.md @@ -329,3 +329,7 @@ function Component() { ## 6.1.2(Oct 30, 2025) - feat: add useScratch hook. + +## 6.1.6(Nov 21, 2025) + +- fix(createStorage): use `useLatest` to avoid unnecessary re-renders and simplify dependency arrays diff --git a/packages/core/package.json b/packages/core/package.json index 9c9b26b4..9f9914f7 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@reactuses/core", - "version": "6.1.5", + "version": "6.1.6", "license": "Unlicense", "homepage": "https://www.reactuse.com/", "repository": { diff --git a/packages/core/src/createStorage/index.ts b/packages/core/src/createStorage/index.ts index 8413e393..e79a6762 100644 --- a/packages/core/src/createStorage/index.ts +++ b/packages/core/src/createStorage/index.ts @@ -5,6 +5,7 @@ import { guessSerializerType } from '../utils/serializer' import { useEvent } from '../useEvent' import { defaultOnError, defaultOptions } from '../utils/defaults' import { useDeepCompareEffect } from '../useDeepCompareEffect' +import { useLatest } from '../useLatest' export interface Serializer { read: (raw: string) => T @@ -131,23 +132,26 @@ export default function useStorage< mountStorageValue, listenToStorageChanges = true, } = options - const storageValue = mountStorageValue ?? effectStorageValue + const storageValueRef = useLatest(mountStorageValue ?? effectStorageValue) + const onErrorRef = useLatest(onError) try { storage = getStorage() } catch (err) { - onError(err) + onErrorRef.current(err) } const type = guessSerializerType(defaultValue) - const serializer = options.serializer ?? StorageSerializers[type] + const serializerRef = useLatest(options.serializer ?? StorageSerializers[type]) const [state, setState] = useState( - getInitialState(key, defaultValue, storage, serializer, onError), + getInitialState(key, defaultValue, storage, serializerRef.current, onError), ) useDeepCompareEffect(() => { + const serializer = serializerRef.current + const storageValue = storageValueRef.current const data = (storageValue ? isFunction(storageValue) @@ -167,12 +171,12 @@ export default function useStorage< } } catch (e) { - onError(e) + onErrorRef.current(e) } } setState(getStoredValue()) - }, [key, serializer, storage, onError, storageValue]) + }, [key, storage]) const updateState: Dispatch> = useEvent( valOrFunc => { @@ -184,10 +188,10 @@ export default function useStorage< } else { try { - storage?.setItem(key, serializer.write(currentState)) + storage?.setItem(key, serializerRef.current.write(currentState)) } catch (e) { - onError(e) + onErrorRef.current(e) } } }, @@ -197,14 +201,14 @@ export default function useStorage< try { const raw = storage?.getItem(key) if (raw !== undefined && raw !== null) { - updateState(serializer.read(raw)) + updateState(serializerRef.current.read(raw)) } else { updateState(null) } } catch (e) { - onError(e) + onErrorRef.current(e) } }) diff --git a/packages/website-docusaurus/docs/changelog.md b/packages/website-docusaurus/docs/changelog.md index a8981454..78b061a8 100644 --- a/packages/website-docusaurus/docs/changelog.md +++ b/packages/website-docusaurus/docs/changelog.md @@ -336,3 +336,7 @@ function Component() { ## 6.1.2(Oct 30, 2025) - feat: add useScratch hook. + +## 6.1.6(Nov 21, 2025) + +- fix(createStorage): use `useLatest` to avoid unnecessary re-renders and simplify dependency arrays diff --git a/packages/website-docusaurus/docs/state/useLocalStorage.mdx b/packages/website-docusaurus/docs/state/useLocalStorage.mdx index f9df44a3..5bf627a9 100644 --- a/packages/website-docusaurus/docs/state/useLocalStorage.mdx +++ b/packages/website-docusaurus/docs/state/useLocalStorage.mdx @@ -14,14 +14,48 @@ React side-effect hook that manages a single `localStorage` key function Demo() { // bind string const [value, setValue] = useLocalStorage("my-key", "key"); + + // bind object with custom serializer + const [myObj, setMyObj] = useLocalStorage( + "myObj", + { + name: "test", + }, + { + serializer: { + read: (val) => { + console.log("read", val); + return JSON.parse(val); + }, + write: (val) => { + console.log("write", val); + return JSON.stringify(val); + }, + }, + } + ); return (
-
Value: {value}
- - - {/* delete data from storage */} - +
+

String Value

+
Value: {value}
+ + + +
+ +
+

Object Value

+
Object: {JSON.stringify(myObj)}
+ + + +
); }; diff --git a/packages/website-docusaurus/i18n/zh-Hans/docusaurus-plugin-content-docs/current/state/useLocalStorage.mdx b/packages/website-docusaurus/i18n/zh-Hans/docusaurus-plugin-content-docs/current/state/useLocalStorage.mdx index f7283d4c..6d52a507 100644 --- a/packages/website-docusaurus/i18n/zh-Hans/docusaurus-plugin-content-docs/current/state/useLocalStorage.mdx +++ b/packages/website-docusaurus/i18n/zh-Hans/docusaurus-plugin-content-docs/current/state/useLocalStorage.mdx @@ -10,18 +10,51 @@ description: 轻松管理 `localStorage`。 本文介绍其用法、最佳实践 ## Usage ```tsx live - function Demo() { - // bind string + // 绑定字符串 const [value, setValue] = useLocalStorage("my-key", "key"); + + // 绑定对象(使用自定义序列化器) + const [myObj, setMyObj] = useLocalStorage( + "myObj", + { + name: "test", + }, + { + serializer: { + read: (val) => { + console.log("读取", val); + return JSON.parse(val); + }, + write: (val) => { + console.log("写入", val); + return JSON.stringify(val); + }, + }, + } + ); return (
-
值: {value}
- - - {/* delete data from storage */} - +
+

字符串值

+
值: {value}
+ + + +
+ +
+

对象值

+
对象: {JSON.stringify(myObj)}
+ + + +
); }; diff --git a/packages/website-docusaurus/i18n/zh-Hant/docusaurus-plugin-content-docs/current/state/useLocalStorage.mdx b/packages/website-docusaurus/i18n/zh-Hant/docusaurus-plugin-content-docs/current/state/useLocalStorage.mdx index b59f22b9..412dd2d9 100644 --- a/packages/website-docusaurus/i18n/zh-Hant/docusaurus-plugin-content-docs/current/state/useLocalStorage.mdx +++ b/packages/website-docusaurus/i18n/zh-Hant/docusaurus-plugin-content-docs/current/state/useLocalStorage.mdx @@ -10,18 +10,51 @@ description: 輕鬆管理 `localStorage`。 本文介紹其用法、最佳實踐 ## Usage ```tsx live - function Demo() { - // bind string + // 綁定字串 const [value, setValue] = useLocalStorage("my-key", "key"); + + // 綁定物件(使用自定義序列化器) + const [myObj, setMyObj] = useLocalStorage( + "myObj", + { + name: "test", + }, + { + serializer: { + read: (val) => { + console.log("讀取", val); + return JSON.parse(val); + }, + write: (val) => { + console.log("寫入", val); + return JSON.stringify(val); + }, + }, + } + ); return (
-
值: {value}
- - - {/* 從存儲中刪除數據 */} - +
+

字串值

+
值: {value}
+ + + +
+ +
+

物件值

+
物件: {JSON.stringify(myObj)}
+ + + +
); };