@geee-be/react-utils 1.0.7 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/dist/hooks/state.util.js +3 -1
- package/dist/hooks/use-broadcast-channel.js +1 -1
- package/dist/hooks/use-history-state.js +3 -5
- package/dist/hooks/use-local-state.js +4 -2
- package/dist/hooks/use-local-state.stories.js +4 -2
- package/package.json +3 -3
- package/src/hooks/index.ts +0 -1
- package/src/hooks/state.util.ts +11 -3
- package/src/hooks/use-broadcast-channel.ts +1 -1
- package/src/hooks/use-history-state.ts +12 -6
- package/src/hooks/use-local-state.stories.tsx +9 -2
- package/src/hooks/use-local-state.ts +12 -3
- package/tsconfig.json +15 -2
package/CHANGELOG.md
CHANGED
package/dist/hooks/state.util.js
CHANGED
@@ -2,7 +2,9 @@ export const deserialize = (serialized, fromSerializable) => {
|
|
2
2
|
if (!serialized || typeof serialized !== 'string')
|
3
3
|
return undefined;
|
4
4
|
try {
|
5
|
-
return fromSerializable
|
5
|
+
return fromSerializable
|
6
|
+
? fromSerializable(JSON.parse(serialized))
|
7
|
+
: JSON.parse(serialized);
|
6
8
|
}
|
7
9
|
catch {
|
8
10
|
return undefined;
|
@@ -22,7 +22,7 @@ import { useCallback, useEffect, useMemo } from 'react';
|
|
22
22
|
*/
|
23
23
|
export const useBroadcastChannel = (channelName, handleMessage, handleMessageError) => {
|
24
24
|
const channel = useMemo(() => typeof window !== 'undefined' && 'BroadcastChannel' in window
|
25
|
-
? new BroadcastChannel(channelName
|
25
|
+
? new BroadcastChannel(`${channelName}-channel`)
|
26
26
|
: null, [channelName]);
|
27
27
|
useChannelEventListener(channel, 'message', handleMessage);
|
28
28
|
useChannelEventListener(channel, 'messageerror', handleMessageError);
|
@@ -3,12 +3,10 @@ import { useEffect, useState } from 'react';
|
|
3
3
|
import { deserialize, getInitialValue, serialize } from './state.util.js';
|
4
4
|
export const useHistoryState = (key, initialValue, replace = true, options) => {
|
5
5
|
if (typeof history === 'undefined') {
|
6
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
7
6
|
return [undefined, () => { }];
|
8
7
|
}
|
9
|
-
const [value, setValue] = useState(
|
10
|
-
|
11
|
-
() => deserialize(history.state?.[key], options?.fromSerializable) ?? getInitialValue(initialValue));
|
8
|
+
const [value, setValue] = useState(() => deserialize(history.state?.[key], options?.fromSerializable) ??
|
9
|
+
getInitialValue(initialValue));
|
12
10
|
useEffect(() => {
|
13
11
|
if (replace) {
|
14
12
|
history.replaceState({ ...history.state, [key]: serialize(value, options?.toSerializable) }, '');
|
@@ -16,6 +14,6 @@ export const useHistoryState = (key, initialValue, replace = true, options) => {
|
|
16
14
|
else {
|
17
15
|
history.pushState({ ...history.state, [key]: serialize(value, options?.toSerializable) }, '');
|
18
16
|
}
|
19
|
-
}, [key, value]);
|
17
|
+
}, [key, value, options?.toSerializable, replace]);
|
20
18
|
return [value, setValue];
|
21
19
|
};
|
@@ -6,7 +6,8 @@ import { deserialize, getInitialValue, serialize } from './state.util.js';
|
|
6
6
|
export const useLocalState = (key, initialValue, options) => {
|
7
7
|
const readValue = useCallback(() => {
|
8
8
|
const item = localStorage.getItem(key);
|
9
|
-
return deserialize(item, options?.fromSerializable) ??
|
9
|
+
return (deserialize(item, options?.fromSerializable) ??
|
10
|
+
getInitialValue(initialValue));
|
10
11
|
}, [key, options?.fromSerializable, initialValue]);
|
11
12
|
const [storedValue, setStoredValue] = useState(readValue);
|
12
13
|
const setValue = useCallback((value) => {
|
@@ -17,7 +18,8 @@ export const useLocalState = (key, initialValue, options) => {
|
|
17
18
|
const storageValueChanged = useCallback((ev) => {
|
18
19
|
if (ev.storageArea !== localStorage || ev.key !== key)
|
19
20
|
return;
|
20
|
-
setStoredValue(deserialize(ev.newValue, options?.fromSerializable) ??
|
21
|
+
setStoredValue(deserialize(ev.newValue, options?.fromSerializable) ??
|
22
|
+
getInitialValue(initialValue));
|
21
23
|
}, [key, options?.fromSerializable, initialValue]);
|
22
24
|
useEffect(() => {
|
23
25
|
window.addEventListener('storage', storageValueChanged);
|
@@ -6,8 +6,10 @@ const meta = {
|
|
6
6
|
export default meta;
|
7
7
|
export const Primary = {
|
8
8
|
render: () => {
|
9
|
-
const [value, setValue] = useLocalState('story-use-local-state', {
|
9
|
+
const [value, setValue] = useLocalState('story-use-local-state', {
|
10
|
+
val: 1,
|
11
|
+
});
|
10
12
|
console.log('render', value);
|
11
|
-
return (_jsxs("div", { children: [JSON.stringify(value), _jsx("button", { onClick: () => setValue((prev) => ({ val: prev.val + 1 ?? -2 })), children: "Inc" })] }));
|
13
|
+
return (_jsxs("div", { children: [JSON.stringify(value), _jsx("button", { type: "button", onClick: () => setValue((prev) => ({ val: prev.val + 1 ?? -2 })), children: "Inc" })] }));
|
12
14
|
},
|
13
15
|
};
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@geee-be/react-utils",
|
3
|
-
"version": "1.
|
3
|
+
"version": "1.1.1",
|
4
4
|
"description": "",
|
5
5
|
"keywords": [],
|
6
6
|
"license": "MIT",
|
@@ -32,8 +32,8 @@
|
|
32
32
|
"prebuild": "rimraf dist/*",
|
33
33
|
"build": "tsc",
|
34
34
|
"build-storybook": "storybook build",
|
35
|
-
"lint": "npm run lint:
|
36
|
-
"lint:
|
35
|
+
"lint": "npm run lint:biome && npm run lint:types && npm run lint:unused-exports",
|
36
|
+
"lint:biome": "biome ci src/",
|
37
37
|
"lint:types": "tsc --noEmit",
|
38
38
|
"lint:unused-exports": "ts-unused-exports tsconfig.unused-exports.json --excludePathsFromReport=src/index.ts",
|
39
39
|
"prestart:dev": "rimraf dist/*",
|
package/src/hooks/index.ts
CHANGED
package/src/hooks/state.util.ts
CHANGED
@@ -3,16 +3,24 @@ export interface SerializationOptions<T, S> {
|
|
3
3
|
toSerializable: (value: T) => S;
|
4
4
|
}
|
5
5
|
|
6
|
-
export const deserialize = <T, S>(
|
6
|
+
export const deserialize = <T, S>(
|
7
|
+
serialized: unknown,
|
8
|
+
fromSerializable?: (value: S) => T,
|
9
|
+
): T | undefined => {
|
7
10
|
if (!serialized || typeof serialized !== 'string') return undefined;
|
8
11
|
try {
|
9
|
-
return fromSerializable
|
12
|
+
return fromSerializable
|
13
|
+
? fromSerializable(JSON.parse(serialized) as S)
|
14
|
+
: (JSON.parse(serialized) as T);
|
10
15
|
} catch {
|
11
16
|
return undefined;
|
12
17
|
}
|
13
18
|
};
|
14
19
|
|
15
|
-
export const serialize = <T, S>(
|
20
|
+
export const serialize = <T, S>(
|
21
|
+
value: T,
|
22
|
+
toSerializable?: (value: T) => S,
|
23
|
+
): string => {
|
16
24
|
try {
|
17
25
|
return JSON.stringify(toSerializable ? toSerializable(value) : value);
|
18
26
|
} catch {
|
@@ -30,7 +30,7 @@ export const useBroadcastChannel = <T = string>(
|
|
30
30
|
const channel = useMemo(
|
31
31
|
() =>
|
32
32
|
typeof window !== 'undefined' && 'BroadcastChannel' in window
|
33
|
-
? new BroadcastChannel(channelName
|
33
|
+
? new BroadcastChannel(`${channelName}-channel`)
|
34
34
|
: null,
|
35
35
|
[channelName],
|
36
36
|
);
|
@@ -12,21 +12,27 @@ export const useHistoryState = <T, S = T>(
|
|
12
12
|
options?: SerializationOptions<T, S>,
|
13
13
|
): [T, Dispatch<SetStateAction<T>>] => {
|
14
14
|
if (typeof history === 'undefined') {
|
15
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
16
15
|
return [undefined as any, () => {}];
|
17
16
|
}
|
18
17
|
const [value, setValue] = useState<T>(
|
19
|
-
|
20
|
-
|
18
|
+
() =>
|
19
|
+
deserialize(history.state?.[key], options?.fromSerializable) ??
|
20
|
+
getInitialValue(initialValue),
|
21
21
|
);
|
22
22
|
|
23
23
|
useEffect(() => {
|
24
24
|
if (replace) {
|
25
|
-
history.replaceState(
|
25
|
+
history.replaceState(
|
26
|
+
{ ...history.state, [key]: serialize(value, options?.toSerializable) },
|
27
|
+
'',
|
28
|
+
);
|
26
29
|
} else {
|
27
|
-
history.pushState(
|
30
|
+
history.pushState(
|
31
|
+
{ ...history.state, [key]: serialize(value, options?.toSerializable) },
|
32
|
+
'',
|
33
|
+
);
|
28
34
|
}
|
29
|
-
}, [key, value]);
|
35
|
+
}, [key, value, options?.toSerializable, replace]);
|
30
36
|
|
31
37
|
return [value, setValue];
|
32
38
|
};
|
@@ -10,12 +10,19 @@ type Story = StoryObj;
|
|
10
10
|
|
11
11
|
export const Primary: Story = {
|
12
12
|
render: () => {
|
13
|
-
const [value, setValue] = useLocalState('story-use-local-state', {
|
13
|
+
const [value, setValue] = useLocalState('story-use-local-state', {
|
14
|
+
val: 1,
|
15
|
+
});
|
14
16
|
console.log('render', value);
|
15
17
|
return (
|
16
18
|
<div>
|
17
19
|
{JSON.stringify(value)}
|
18
|
-
<button
|
20
|
+
<button
|
21
|
+
type="button"
|
22
|
+
onClick={() => setValue((prev) => ({ val: prev.val + 1 ?? -2 }))}
|
23
|
+
>
|
24
|
+
Inc
|
25
|
+
</button>
|
19
26
|
</div>
|
20
27
|
);
|
21
28
|
},
|
@@ -16,7 +16,10 @@ export const useLocalState = <T, S = T>(
|
|
16
16
|
): [T, SetValue<T>] => {
|
17
17
|
const readValue = useCallback((): T => {
|
18
18
|
const item = localStorage.getItem(key);
|
19
|
-
return
|
19
|
+
return (
|
20
|
+
deserialize<T, S>(item, options?.fromSerializable) ??
|
21
|
+
getInitialValue(initialValue)
|
22
|
+
);
|
20
23
|
}, [key, options?.fromSerializable, initialValue]);
|
21
24
|
|
22
25
|
const [storedValue, setStoredValue] = useState<T>(readValue);
|
@@ -25,7 +28,10 @@ export const useLocalState = <T, S = T>(
|
|
25
28
|
(value) => {
|
26
29
|
const newValue = value instanceof Function ? value(storedValue) : value;
|
27
30
|
setStoredValue(newValue);
|
28
|
-
localStorage.setItem(
|
31
|
+
localStorage.setItem(
|
32
|
+
key,
|
33
|
+
serialize<T, S>(newValue, options?.toSerializable),
|
34
|
+
);
|
29
35
|
},
|
30
36
|
[key, options?.toSerializable, storedValue],
|
31
37
|
);
|
@@ -33,7 +39,10 @@ export const useLocalState = <T, S = T>(
|
|
33
39
|
const storageValueChanged = useCallback(
|
34
40
|
(ev: StorageEvent) => {
|
35
41
|
if (ev.storageArea !== localStorage || ev.key !== key) return;
|
36
|
-
setStoredValue(
|
42
|
+
setStoredValue(
|
43
|
+
deserialize<T, S>(ev.newValue, options?.fromSerializable) ??
|
44
|
+
getInitialValue(initialValue),
|
45
|
+
);
|
37
46
|
},
|
38
47
|
[key, options?.fromSerializable, initialValue],
|
39
48
|
);
|
package/tsconfig.json
CHANGED
@@ -1,8 +1,21 @@
|
|
1
1
|
{
|
2
|
-
"extends": "../create/tsconfig.web.json",
|
3
2
|
"compilerOptions": {
|
3
|
+
"allowJs": false,
|
4
|
+
"allowSyntheticDefaultImports": true,
|
5
|
+
"esModuleInterop": false,
|
6
|
+
"forceConsistentCasingInFileNames": true,
|
7
|
+
"isolatedModules": true,
|
8
|
+
"jsx": "react-jsx",
|
9
|
+
"lib": ["DOM", "DOM.Iterable", "ESNext"],
|
10
|
+
"module": "ESNext",
|
11
|
+
"moduleResolution": "Node",
|
4
12
|
"noEmit": false,
|
5
|
-
"outDir": "./dist"
|
13
|
+
"outDir": "./dist",
|
14
|
+
"resolveJsonModule": true,
|
15
|
+
"skipLibCheck": true,
|
16
|
+
"strict": true,
|
17
|
+
"target": "ESNext",
|
18
|
+
"useDefineForClassFields": true
|
6
19
|
},
|
7
20
|
"include": ["./src"]
|
8
21
|
}
|