From 5fbdefd416b7dbd44115adb19e294418e15d06f9 Mon Sep 17 00:00:00 2001 From: vaakian Date: Sat, 19 Aug 2023 21:55:32 +0800 Subject: [PATCH 1/7] feat: sync `storage` state --- .../hooks/src/createUseStorageState/index.ts | 16 +++++++++ packages/hooks/src/useEventListener/index.ts | 5 +++ .../__tests__/index.test.ts | 20 ++++++++--- .../src/useLocalStorageState/demo/demo4.tsx | 33 +++++++++++++++++++ .../src/useLocalStorageState/index.en-US.md | 4 +++ .../src/useLocalStorageState/index.zh-CN.md | 4 +++ 6 files changed, 78 insertions(+), 4 deletions(-) create mode 100644 packages/hooks/src/useLocalStorageState/demo/demo4.tsx diff --git a/packages/hooks/src/createUseStorageState/index.ts b/packages/hooks/src/createUseStorageState/index.ts index cf184ebd5a..5c30c8064d 100644 --- a/packages/hooks/src/createUseStorageState/index.ts +++ b/packages/hooks/src/createUseStorageState/index.ts @@ -1,8 +1,11 @@ import { useState } from 'react'; +import useEventListener from '../useEventListener'; import useMemoizedFn from '../useMemoizedFn'; import useUpdateEffect from '../useUpdateEffect'; import { isFunction, isUndef } from '../utils'; +const SYNC_STORAGE_EVENT = 'AHOOKS_SYNC_STORAGE_EVENT'; + export type SetState = S | ((prevState?: S) => S); export interface Options { @@ -76,8 +79,21 @@ export function createUseStorageState(getStorage: () => Storage | undefined) { console.error(e); } } + dispatchEvent(new StorageEvent(SYNC_STORAGE_EVENT, { key })); + }; + + const syncStorageState = (event: StorageEvent) => { + if (event.key === key) { + setState(getStoredValue()); + } }; + // from different tabs + useEventListener('storage', syncStorageState); + + // from the same tab but different hooks + useEventListener(SYNC_STORAGE_EVENT, syncStorageState); + return [state, useMemoizedFn(updateState)] as const; } return useStorageState; diff --git a/packages/hooks/src/useEventListener/index.ts b/packages/hooks/src/useEventListener/index.ts index 12a8eb60b1..36f138966f 100644 --- a/packages/hooks/src/useEventListener/index.ts +++ b/packages/hooks/src/useEventListener/index.ts @@ -34,6 +34,11 @@ function useEventListener( handler: (ev: WindowEventMap[K]) => void, options?: Options, ): void; +function useEventListener( + eventName: string, + handler: (event: Event) => void, + options?: Options, +): void; function useEventListener(eventName: string, handler: noop, options: Options): void; function useEventListener(eventName: string, handler: noop, options: Options = {}) { diff --git a/packages/hooks/src/useLocalStorageState/__tests__/index.test.ts b/packages/hooks/src/useLocalStorageState/__tests__/index.test.ts index d7151a52ce..9ff09b6f77 100644 --- a/packages/hooks/src/useLocalStorageState/__tests__/index.test.ts +++ b/packages/hooks/src/useLocalStorageState/__tests__/index.test.ts @@ -25,7 +25,7 @@ describe('useLocalStorageState', () => { anotherHook.result.current.setState('C'); }); expect(anotherHook.result.current.state).toBe('C'); - expect(hook.result.current.state).toBe('B'); + expect(hook.result.current.state).toBe('C'); }); it('should support object', () => { @@ -48,7 +48,7 @@ describe('useLocalStorageState', () => { }); }); expect(anotherHook.result.current.state).toEqual({ name: 'C' }); - expect(hook.result.current.state).toEqual({ name: 'B' }); + expect(hook.result.current.state).toEqual({ name: 'C' }); }); it('should support number', () => { @@ -65,7 +65,7 @@ describe('useLocalStorageState', () => { anotherHook.result.current.setState(3); }); expect(anotherHook.result.current.state).toBe(3); - expect(hook.result.current.state).toBe(2); + expect(hook.result.current.state).toBe(3); }); it('should support boolean', () => { @@ -82,7 +82,7 @@ describe('useLocalStorageState', () => { anotherHook.result.current.setState(true); }); expect(anotherHook.result.current.state).toBe(true); - expect(hook.result.current.state).toBe(false); + expect(hook.result.current.state).toBe(true); }); it('should support null', () => { @@ -106,4 +106,16 @@ describe('useLocalStorageState', () => { }); expect(hook.result.current.state).toBe('hello world, zhangsan'); }); + + it('should sync state when changes', async () => { + const LOCAL_STORAGE_KEY = 'test-sync-state'; + const hook = setUp(LOCAL_STORAGE_KEY, 'foo'); + const anotherHook = setUp(LOCAL_STORAGE_KEY, 'foo'); + expect(hook.result.current.state).toBe('foo'); + act(() => { + hook.result.current.setState('bar'); + }); + expect(hook.result.current.state).toBe('bar'); + expect(anotherHook.result.current.state).toBe('bar'); + }); }); diff --git a/packages/hooks/src/useLocalStorageState/demo/demo4.tsx b/packages/hooks/src/useLocalStorageState/demo/demo4.tsx new file mode 100644 index 0000000000..56a28f6b9f --- /dev/null +++ b/packages/hooks/src/useLocalStorageState/demo/demo4.tsx @@ -0,0 +1,33 @@ +/** + * title: Sync state with localStorage + * desc: The state can be synchronized with `localStorage`, ensuring that it remains consistent across different tabs or when modified elsewhere.(try to open this demo in two tabs, and then click the button in one of the tabs) + * + * title.zh-CN: 将 state 与 localStorage 保持同步 + * desc.zh-CN: 当 state 在其他地方被修改时,会自动同步到 `localStorage` 中,包括不同浏览器 tab 之间。(尝试打开两个此页面,点击其中一个页面的按钮,另一个页面的 count 会自动更新) + */ + +import React from 'react'; +import { useLocalStorageState } from 'ahooks'; + +export default function () { + return ( + <> + + + + ); +} + +function Counter() { + const [count, setCount] = useLocalStorageState('use-local-storage-state-demo4', { + defaultValue: 0, + }); + + const add = () => setCount(count! + 1); + + return ( + + ); +} diff --git a/packages/hooks/src/useLocalStorageState/index.en-US.md b/packages/hooks/src/useLocalStorageState/index.en-US.md index 940d23bcd3..db7296600c 100644 --- a/packages/hooks/src/useLocalStorageState/index.en-US.md +++ b/packages/hooks/src/useLocalStorageState/index.en-US.md @@ -21,6 +21,10 @@ A Hook that store state into localStorage. +### Sync state with localStorage + + + ## API If you want to delete this record from localStorage, you can use `setState()` or `setState(undefined)`. diff --git a/packages/hooks/src/useLocalStorageState/index.zh-CN.md b/packages/hooks/src/useLocalStorageState/index.zh-CN.md index 752bf38a57..40300e9965 100644 --- a/packages/hooks/src/useLocalStorageState/index.zh-CN.md +++ b/packages/hooks/src/useLocalStorageState/index.zh-CN.md @@ -21,6 +21,10 @@ nav: +### 将 state 与 localStorage 保持同步 + + + ## API 如果想从 localStorage 中删除这条数据,可以使用 `setState()` 或 `setState(undefined)` 。 From 81ed113ff5e0c7c822ff6edffa7e0f34d578b265 Mon Sep 17 00:00:00 2001 From: vaakian Date: Sat, 19 Aug 2023 21:59:51 +0800 Subject: [PATCH 2/7] chore: update test case --- .../hooks/src/useSessionStorageState/__tests__/index.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/hooks/src/useSessionStorageState/__tests__/index.test.ts b/packages/hooks/src/useSessionStorageState/__tests__/index.test.ts index d7fab6d2c1..c57553a485 100644 --- a/packages/hooks/src/useSessionStorageState/__tests__/index.test.ts +++ b/packages/hooks/src/useSessionStorageState/__tests__/index.test.ts @@ -31,7 +31,7 @@ describe('useSessionStorageState', () => { }); }); expect(anotherHook.result.current.state).toEqual({ name: 'C' }); - expect(hook.result.current.state).toEqual({ name: 'B' }); + expect(hook.result.current.state).toEqual({ name: 'C' }); }); it('should support function updater', () => { From 35e9d7e2ed51a28686f97e5450773589ccfa1e31 Mon Sep 17 00:00:00 2001 From: liuyib <1656081615@qq.com> Date: Mon, 21 Aug 2023 14:58:01 +0800 Subject: [PATCH 3/7] refactor: replace StorageEvent to CustomEvent, and add listenStorageChange param --- .../hooks/src/createUseStorageState/index.ts | 62 ++++++++++++++----- packages/hooks/src/useEventListener/index.ts | 11 +++- .../__tests__/index.test.ts | 33 ++++++---- .../src/useLocalStorageState/demo/demo4.tsx | 14 +++-- 4 files changed, 85 insertions(+), 35 deletions(-) diff --git a/packages/hooks/src/createUseStorageState/index.ts b/packages/hooks/src/createUseStorageState/index.ts index 5c30c8064d..c4f8e90df3 100644 --- a/packages/hooks/src/createUseStorageState/index.ts +++ b/packages/hooks/src/createUseStorageState/index.ts @@ -4,12 +4,13 @@ import useMemoizedFn from '../useMemoizedFn'; import useUpdateEffect from '../useUpdateEffect'; import { isFunction, isUndef } from '../utils'; -const SYNC_STORAGE_EVENT = 'AHOOKS_SYNC_STORAGE_EVENT'; +export const SYNC_STORAGE_EVENT_NAME = 'AHOOKS_SYNC_STORAGE_EVENT_NAME'; export type SetState = S | ((prevState?: S) => S); export interface Options { defaultValue?: T | (() => T); + listenStorageChange?: boolean; serializer?: (value: T) => string; deserializer?: (value: string) => T; onError?: (error: unknown) => void; @@ -19,6 +20,7 @@ export function createUseStorageState(getStorage: () => Storage | undefined) { function useStorageState(key: string, options: Options = {}) { let storage: Storage | undefined; const { + listenStorageChange = false, onError = (e) => { console.error(e); }, @@ -70,29 +72,57 @@ export function createUseStorageState(getStorage: () => Storage | undefined) { const currentState = isFunction(value) ? value(state) : value; setState(currentState); - if (isUndef(currentState)) { - storage?.removeItem(key); - } else { - try { - storage?.setItem(key, serializer(currentState)); - } catch (e) { - console.error(e); + try { + let newValue: string | null; + const oldValue = storage?.getItem(key); + + if (isUndef(currentState)) { + newValue = null; + storage?.removeItem(key); + } else { + newValue = serializer(currentState); + storage?.setItem(key, newValue); } + + dispatchEvent( + // send custom event to communicate within same page + // importantly this should not be a StorageEvent since those cannot + // be constructed with a non-built-in storage area + new CustomEvent(SYNC_STORAGE_EVENT_NAME, { + detail: { + key, + newValue, + oldValue, + storageArea: storage, + }, + }), + ); + } catch (e) { + onError(e); } - dispatchEvent(new StorageEvent(SYNC_STORAGE_EVENT, { key })); }; - const syncStorageState = (event: StorageEvent) => { - if (event.key === key) { - setState(getStoredValue()); + const syncState = (event: StorageEvent) => { + if (event.key !== key || event.storageArea !== storage) { + return; } + + setState(getStoredValue()); + }; + + const syncStateFromCustomEvent = (event: CustomEvent) => { + syncState(event.detail); }; - // from different tabs - useEventListener('storage', syncStorageState); + // from another document + useEventListener('storage', syncState, { + enable: listenStorageChange, + }); - // from the same tab but different hooks - useEventListener(SYNC_STORAGE_EVENT, syncStorageState); + // from the same document but different hooks + useEventListener(SYNC_STORAGE_EVENT_NAME, syncStateFromCustomEvent, { + enable: listenStorageChange, + }); return [state, useMemoizedFn(updateState)] as const; } diff --git a/packages/hooks/src/useEventListener/index.ts b/packages/hooks/src/useEventListener/index.ts index 36f138966f..b066846722 100644 --- a/packages/hooks/src/useEventListener/index.ts +++ b/packages/hooks/src/useEventListener/index.ts @@ -12,6 +12,7 @@ type Options = { capture?: boolean; once?: boolean; passive?: boolean; + enable?: boolean; }; function useEventListener( @@ -37,15 +38,21 @@ function useEventListener( function useEventListener( eventName: string, handler: (event: Event) => void, - options?: Options, + options?: Options, ): void; function useEventListener(eventName: string, handler: noop, options: Options): void; function useEventListener(eventName: string, handler: noop, options: Options = {}) { + const { enable = true } = options; + const handlerRef = useLatest(handler); useEffectWithTarget( () => { + if (!enable) { + return; + } + const targetElement = getTargetElement(options.target, window); if (!targetElement?.addEventListener) { return; @@ -67,7 +74,7 @@ function useEventListener(eventName: string, handler: noop, options: Options = { }); }; }, - [eventName, options.capture, options.once, options.passive], + [eventName, options.capture, options.once, options.passive, enable], options.target, ); } diff --git a/packages/hooks/src/useLocalStorageState/__tests__/index.test.ts b/packages/hooks/src/useLocalStorageState/__tests__/index.test.ts index 9ff09b6f77..1368ebe922 100644 --- a/packages/hooks/src/useLocalStorageState/__tests__/index.test.ts +++ b/packages/hooks/src/useLocalStorageState/__tests__/index.test.ts @@ -1,10 +1,14 @@ import { renderHook, act } from '@testing-library/react'; +import type { Options } from '../../createUseStorageState'; import useLocalStorageState from '../index'; describe('useLocalStorageState', () => { - const setUp = (key: string, value: T) => + const setUp = (key: string, value: T, options?: Options) => renderHook(() => { - const [state, setState] = useLocalStorageState(key, { defaultValue: value }); + const [state, setState] = useLocalStorageState(key, { + defaultValue: value, + ...options, + }); return { state, setState, @@ -25,7 +29,7 @@ describe('useLocalStorageState', () => { anotherHook.result.current.setState('C'); }); expect(anotherHook.result.current.state).toBe('C'); - expect(hook.result.current.state).toBe('C'); + expect(hook.result.current.state).toBe('B'); }); it('should support object', () => { @@ -48,7 +52,7 @@ describe('useLocalStorageState', () => { }); }); expect(anotherHook.result.current.state).toEqual({ name: 'C' }); - expect(hook.result.current.state).toEqual({ name: 'C' }); + expect(hook.result.current.state).toEqual({ name: 'B' }); }); it('should support number', () => { @@ -65,7 +69,7 @@ describe('useLocalStorageState', () => { anotherHook.result.current.setState(3); }); expect(anotherHook.result.current.state).toBe(3); - expect(hook.result.current.state).toBe(3); + expect(hook.result.current.state).toBe(2); }); it('should support boolean', () => { @@ -82,7 +86,7 @@ describe('useLocalStorageState', () => { anotherHook.result.current.setState(true); }); expect(anotherHook.result.current.state).toBe(true); - expect(hook.result.current.state).toBe(true); + expect(hook.result.current.state).toBe(false); }); it('should support null', () => { @@ -109,13 +113,18 @@ describe('useLocalStorageState', () => { it('should sync state when changes', async () => { const LOCAL_STORAGE_KEY = 'test-sync-state'; - const hook = setUp(LOCAL_STORAGE_KEY, 'foo'); - const anotherHook = setUp(LOCAL_STORAGE_KEY, 'foo'); + const hook = setUp(LOCAL_STORAGE_KEY, 'foo', { listenStorageChange: true }); + const anotherHook = setUp(LOCAL_STORAGE_KEY, 'bar', { listenStorageChange: true }); + expect(hook.result.current.state).toBe('foo'); - act(() => { - hook.result.current.setState('bar'); - }); - expect(hook.result.current.state).toBe('bar'); expect(anotherHook.result.current.state).toBe('bar'); + + act(() => hook.result.current.setState('baz')); + expect(hook.result.current.state).toBe('baz'); + expect(anotherHook.result.current.state).toBe('baz'); + + act(() => anotherHook.result.current.setState('qux')); + expect(hook.result.current.state).toBe('qux'); + expect(anotherHook.result.current.state).toBe('qux'); }); }); diff --git a/packages/hooks/src/useLocalStorageState/demo/demo4.tsx b/packages/hooks/src/useLocalStorageState/demo/demo4.tsx index 56a28f6b9f..86985b2493 100644 --- a/packages/hooks/src/useLocalStorageState/demo/demo4.tsx +++ b/packages/hooks/src/useLocalStorageState/demo/demo4.tsx @@ -1,9 +1,9 @@ /** * title: Sync state with localStorage - * desc: The state can be synchronized with `localStorage`, ensuring that it remains consistent across different tabs or when modified elsewhere.(try to open this demo in two tabs, and then click the button in one of the tabs) + * desc: The state can be synchronized with `localStorage`, ensuring that it remains consistent across different tabs or when modified elsewhere (try to open this demo in two tabs, and then click the button in one of the tabs) * * title.zh-CN: 将 state 与 localStorage 保持同步 - * desc.zh-CN: 当 state 在其他地方被修改时,会自动同步到 `localStorage` 中,包括不同浏览器 tab 之间。(尝试打开两个此页面,点击其中一个页面的按钮,另一个页面的 count 会自动更新) + * desc.zh-CN: 当 state 在其他地方被修改时,会自动同步到 `localStorage` 中,包括同一浏览器不同 tab 之间(尝试打开两个此页面,点击其中一个页面的按钮,另一个页面的 count 会自动更新) */ import React from 'react'; @@ -24,10 +24,14 @@ function Counter() { }); const add = () => setCount(count! + 1); + const clear = () => setCount(); return ( - +
+ + +
); } From ca342d1b85d24b37fd51c73be6541c2df50c063c Mon Sep 17 00:00:00 2001 From: liuyib <1656081615@qq.com> Date: Mon, 21 Aug 2023 15:08:22 +0800 Subject: [PATCH 4/7] test: revert case in useSessionStorage --- .../hooks/src/useSessionStorageState/__tests__/index.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/hooks/src/useSessionStorageState/__tests__/index.test.ts b/packages/hooks/src/useSessionStorageState/__tests__/index.test.ts index c57553a485..d7fab6d2c1 100644 --- a/packages/hooks/src/useSessionStorageState/__tests__/index.test.ts +++ b/packages/hooks/src/useSessionStorageState/__tests__/index.test.ts @@ -31,7 +31,7 @@ describe('useSessionStorageState', () => { }); }); expect(anotherHook.result.current.state).toEqual({ name: 'C' }); - expect(hook.result.current.state).toEqual({ name: 'C' }); + expect(hook.result.current.state).toEqual({ name: 'B' }); }); it('should support function updater', () => { From de1df4d64f470058d963d66a4a92f86aa9e75ce9 Mon Sep 17 00:00:00 2001 From: liuyib <1656081615@qq.com> Date: Mon, 21 Aug 2023 16:05:45 +0800 Subject: [PATCH 5/7] docs: modify docs and demo --- packages/hooks/src/useEventListener/index.en-US.md | 1 + packages/hooks/src/useEventListener/index.zh-CN.md | 1 + .../hooks/src/useLocalStorageState/demo/demo4.tsx | 5 +++-- .../hooks/src/useLocalStorageState/index.en-US.md | 13 +++++++------ .../hooks/src/useLocalStorageState/index.zh-CN.md | 13 +++++++------ 5 files changed, 19 insertions(+), 14 deletions(-) diff --git a/packages/hooks/src/useEventListener/index.en-US.md b/packages/hooks/src/useEventListener/index.en-US.md index 2e89dd2404..20b3520a2f 100644 --- a/packages/hooks/src/useEventListener/index.en-US.md +++ b/packages/hooks/src/useEventListener/index.en-US.md @@ -43,3 +43,4 @@ useEventListener( | capture | Optional, a Boolean indicating that events of this type will be dispatched to the registered listener before being dispatched to any EventTarget beneath it in the DOM tree. | `boolean` | `false` | | once | Optional, A Boolean indicating that the listener should be invoked at most once after being added. If true, the listener would be automatically removed when invoked. | `boolean` | `false` | | passive | Optional, A Boolean which, if true, indicates that the function specified by listener will never call preventDefault(). If a passive listener does call preventDefault(), the user agent will do nothing other than generate a console warning. | `boolean` | `false` | +| enable | Optional, Whether to enable listening. | `boolean` | `true` | diff --git a/packages/hooks/src/useEventListener/index.zh-CN.md b/packages/hooks/src/useEventListener/index.zh-CN.md index 35af05bcce..b3606f1046 100644 --- a/packages/hooks/src/useEventListener/index.zh-CN.md +++ b/packages/hooks/src/useEventListener/index.zh-CN.md @@ -43,3 +43,4 @@ useEventListener( | capture | 可选项,listener 会在该类型的事件捕获阶段传播到该 EventTarget 时触发。 | `boolean` | `false` | | once | 可选项,listener 在添加之后最多只调用一次。如果是 true,listener 会在其被调用之后自动移除。 | `boolean` | `false` | | passive | 可选项,设置为 true 时,表示 listener 永远不会调用 preventDefault() 。如果 listener 仍然调用了这个函数,客户端将会忽略它并抛出一个控制台警告。 | `boolean` | `false` | +| enable | 可选项,是否开启监听。 | `boolean` | `true` | diff --git a/packages/hooks/src/useLocalStorageState/demo/demo4.tsx b/packages/hooks/src/useLocalStorageState/demo/demo4.tsx index 86985b2493..ca1c914229 100644 --- a/packages/hooks/src/useLocalStorageState/demo/demo4.tsx +++ b/packages/hooks/src/useLocalStorageState/demo/demo4.tsx @@ -1,9 +1,9 @@ /** * title: Sync state with localStorage - * desc: The state can be synchronized with `localStorage`, ensuring that it remains consistent across different tabs or when modified elsewhere (try to open this demo in two tabs, and then click the button in one of the tabs) + * desc: When the stored value changes, all `useLocalStorageState` with the same `key` will synchronize their states, including different tabs of the same browser (try to open two tabs of this page, clicking a button on one page will automatically update the "count" on the other page). * * title.zh-CN: 将 state 与 localStorage 保持同步 - * desc.zh-CN: 当 state 在其他地方被修改时,会自动同步到 `localStorage` 中,包括同一浏览器不同 tab 之间(尝试打开两个此页面,点击其中一个页面的按钮,另一个页面的 count 会自动更新) + * desc.zh-CN: 存储值变化时,所有 `key` 相同的 `useLocalStorageState` 会同步状态,包括同一浏览器不同 tab 之间(尝试打开两个此页面,点击其中一个页面的按钮,另一个页面的 count 会自动更新) */ import React from 'react'; @@ -21,6 +21,7 @@ export default function () { function Counter() { const [count, setCount] = useLocalStorageState('use-local-storage-state-demo4', { defaultValue: 0, + listenStorageChange: true, }); const add = () => setCount(count! + 1); diff --git a/packages/hooks/src/useLocalStorageState/index.en-US.md b/packages/hooks/src/useLocalStorageState/index.en-US.md index db7296600c..f0001eaacf 100644 --- a/packages/hooks/src/useLocalStorageState/index.en-US.md +++ b/packages/hooks/src/useLocalStorageState/index.en-US.md @@ -54,12 +54,13 @@ const [state, setState] = useLocalStorageState( ### Options -| Property | Description | Type | Default | -| ------------ | ----------------------------- | -------------------------- | ----------------------------- | -| defaultValue | Default value | `any \| (() => any)` | - | -| serializer | Custom serialization method | `(value: any) => string` | `JSON.stringify` | -| deserializer | Custom deserialization method | `(value: string) => any` | `JSON.parse` | -| onError | On error callback | `(error: unknown) => void` | `(e) => { console.error(e) }` | +| Property | Description | Type | Default | +| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------- | ----------------------------- | +| defaultValue | Default value | `any \| (() => any)` | - | +| listenStorageChange | Whether to listen storage changes. If `true`, when the stored value changes, all `useLocalStorageState` with the same `key` will synchronize their states, including different tabs of the same browser | `boolean` | `false` | +| serializer | Custom serialization method | `(value: any) => string` | `JSON.stringify` | +| deserializer | Custom deserialization method | `(value: string) => any` | `JSON.parse` | +| onError | On error callback | `(error: unknown) => void` | `(e) => { console.error(e) }` | ## Remark diff --git a/packages/hooks/src/useLocalStorageState/index.zh-CN.md b/packages/hooks/src/useLocalStorageState/index.zh-CN.md index 40300e9965..a9b84ba3f0 100644 --- a/packages/hooks/src/useLocalStorageState/index.zh-CN.md +++ b/packages/hooks/src/useLocalStorageState/index.zh-CN.md @@ -54,12 +54,13 @@ const [state, setState] = useLocalStorageState( ### Options -| 参数 | 说明 | 类型 | 默认值 | -| ------------ | ------------------ | -------------------------- | ----------------------------- | -| defaultValue | 默认值 | `any \| (() => any)` | - | -| serializer | 自定义序列化方法 | `(value: any) => string` | `JSON.stringify` | -| deserializer | 自定义反序列化方法 | `(value: string) => any` | `JSON.parse` | -| onError | 错误回调函数 | `(error: unknown) => void` | `(e) => { console.error(e) }` | +| 参数 | 说明 | 类型 | 默认值 | +| ------------------- | --------------------------------------------------------------------------------------------------------------------------------- | -------------------------- | ----------------------------- | +| defaultValue | 默认值 | `any \| (() => any)` | - | +| listenStorageChange | 是否监听存储变化。如果是 `true`,当存储值变化时,所有 `key` 相同的 `useLocalStorageState` 会同步状态,包括同一浏览器不同 tab 之间 | `boolean` | `false` | +| serializer | 自定义序列化方法 | `(value: any) => string` | `JSON.stringify` | +| deserializer | 自定义反序列化方法 | `(value: string) => any` | `JSON.parse` | +| onError | 错误回调函数 | `(error: unknown) => void` | `(e) => { console.error(e) }` | ## 备注 From 6b4388d5e2fdd4fdc85de0bab055dea1b61daa5f Mon Sep 17 00:00:00 2001 From: liuyib <1656081615@qq.com> Date: Tue, 22 Aug 2023 10:53:04 +0800 Subject: [PATCH 6/7] test: add case for 'enable' parameter --- .../useEventListener/__tests__/index.test.ts | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/packages/hooks/src/useEventListener/__tests__/index.test.ts b/packages/hooks/src/useEventListener/__tests__/index.test.ts index ae7cc9c08a..009ffa317d 100644 --- a/packages/hooks/src/useEventListener/__tests__/index.test.ts +++ b/packages/hooks/src/useEventListener/__tests__/index.test.ts @@ -31,4 +31,24 @@ describe('useEventListener', () => { document.body.click(); expect(state).toBe(1); }); + + it('test "enable" parameter', async () => { + let state: number = 0; + let enable = true; + const onClick = () => state++; + const { rerender, unmount } = renderHook(() => + useEventListener('click', onClick, { target: () => container, enable }), + ); + + document.body.click(); + expect(state).toBe(0); + container.click(); + expect(state).toBe(1); + + enable = false; + rerender(); + container.click(); + expect(state).toBe(1); + unmount(); + }); }); From c8518f9326919db0cea61cf7b55f47becb1d4930 Mon Sep 17 00:00:00 2001 From: liuyib <1656081615@qq.com> Date: Tue, 22 Aug 2023 10:54:32 +0800 Subject: [PATCH 7/7] test: remove unnecessary code --- packages/hooks/src/useEventListener/__tests__/index.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/hooks/src/useEventListener/__tests__/index.test.ts b/packages/hooks/src/useEventListener/__tests__/index.test.ts index 009ffa317d..57094df80b 100644 --- a/packages/hooks/src/useEventListener/__tests__/index.test.ts +++ b/packages/hooks/src/useEventListener/__tests__/index.test.ts @@ -32,8 +32,8 @@ describe('useEventListener', () => { expect(state).toBe(1); }); - it('test "enable" parameter', async () => { - let state: number = 0; + it('test "enable" parameter', () => { + let state = 0; let enable = true; const onClick = () => state++; const { rerender, unmount } = renderHook(() =>