@variocube/app-ui 1.16.0 → 1.16.2
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/esm/storage/useStorage.js +12 -1
- package/esm/storage/useStorage.js.map +1 -1
- package/esm/storage/useStorage.spec.d.ts +4 -0
- package/esm/storage/useStorage.spec.js +61 -0
- package/esm/storage/useStorage.spec.js.map +1 -0
- package/package.json +2 -2
- package/src/storage/useStorage.spec.tsx +86 -0
- package/src/storage/useStorage.ts +11 -1
|
@@ -14,7 +14,18 @@ export function useStorage(key, defaultValue, storageType) {
|
|
|
14
14
|
storage.addChangeListener(key, updateStateFromStorage);
|
|
15
15
|
return () => storage.removeChangeListener(key, updateStateFromStorage);
|
|
16
16
|
}, [key, updateStateFromStorage]);
|
|
17
|
-
const typedValue = useMemo(() =>
|
|
17
|
+
const typedValue = useMemo(() => {
|
|
18
|
+
if (value === undefined) {
|
|
19
|
+
return defaultValueSerialized !== undefined ? JSON.parse(defaultValueSerialized) : undefined;
|
|
20
|
+
}
|
|
21
|
+
try {
|
|
22
|
+
return JSON.parse(value);
|
|
23
|
+
}
|
|
24
|
+
catch (e) {
|
|
25
|
+
console.warn(`Failed to parse storage value for key "${key}", falling back to default value.`, e);
|
|
26
|
+
return defaultValueSerialized !== undefined ? JSON.parse(defaultValueSerialized) : undefined;
|
|
27
|
+
}
|
|
28
|
+
}, [value, key, defaultValueSerialized]);
|
|
18
29
|
const setTypedValue = useCallback((newValue) => {
|
|
19
30
|
const value = JSON.stringify(newValue);
|
|
20
31
|
if (value != defaultValueSerialized) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useStorage.js","sourceRoot":"","sources":["../../src/storage/useStorage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,WAAW,EAAE,eAAe,EAAE,OAAO,EAAE,QAAQ,EAAC,MAAM,OAAO,CAAC;AACtE,OAAO,EAAC,OAAO,EAAC,MAAM,WAAW,CAAC;AAGlC,MAAM,UAAU,UAAU,CAAI,GAAW,EAAE,YAAe,EAAE,WAAyB;IACpF,MAAM,sBAAsB,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC;IAE3F,MAAM,oBAAoB,GAAG,WAAW,CAAC,GAAG,EAAE;QAC7C,MAAM,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;QACpD,OAAO,YAAY,aAAZ,YAAY,cAAZ,YAAY,GAAI,sBAAsB,CAAC;IAC/C,CAAC,EAAE,CAAC,GAAG,EAAE,sBAAsB,EAAE,WAAW,CAAC,CAAC,CAAC;IAE/C,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAC,oBAAoB,CAAC,CAAC;IAEzD,MAAM,sBAAsB,GAAG,WAAW,CAAC,GAAG,EAAE;QAC/C,QAAQ,CAAC,oBAAoB,EAAE,CAAC,CAAC;IAClC,CAAC,EAAE,CAAC,oBAAoB,CAAC,CAAC,CAAC;IAE3B,eAAe,CAAC,GAAG,EAAE;QACpB,OAAO,CAAC,iBAAiB,CAAC,GAAG,EAAE,sBAAsB,CAAC,CAAC;QACvD,OAAO,GAAG,EAAE,CAAC,OAAO,CAAC,oBAAoB,CAAC,GAAG,EAAE,sBAAsB,CAAC,CAAC;IACxE,CAAC,EAAE,CAAC,GAAG,EAAE,sBAAsB,CAAC,CAAC,CAAC;IAElC,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"useStorage.js","sourceRoot":"","sources":["../../src/storage/useStorage.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jest-environment jsdom
|
|
3
|
+
*/
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
import { useRef } from "react";
|
|
6
|
+
import { act, create } from "react-test-renderer";
|
|
7
|
+
import { useStorage } from "./useStorage";
|
|
8
|
+
// Reset storage between tests
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
localStorage.clear();
|
|
11
|
+
sessionStorage.clear();
|
|
12
|
+
});
|
|
13
|
+
function TestComponent({ storageKey }) {
|
|
14
|
+
const renderCount = useRef(0);
|
|
15
|
+
renderCount.current++;
|
|
16
|
+
// Inline object literal: new reference every render (the bug trigger)
|
|
17
|
+
const [value] = useStorage(storageKey, { page: 0, size: 25 });
|
|
18
|
+
return (React.createElement("div", { "data-render-count": renderCount.current, "data-value": JSON.stringify(value) }));
|
|
19
|
+
}
|
|
20
|
+
function getTestResult(renderer) {
|
|
21
|
+
const div = renderer.root.findByType("div");
|
|
22
|
+
return {
|
|
23
|
+
renderCount: Number(div.props["data-render-count"]),
|
|
24
|
+
value: JSON.parse(div.props["data-value"]),
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
describe("useStorage", () => {
|
|
28
|
+
test("does not cause infinite re-renders with unstable object defaultValue", () => {
|
|
29
|
+
let renderer;
|
|
30
|
+
act(() => {
|
|
31
|
+
renderer = create(React.createElement(TestComponent, { storageKey: "test-key" }));
|
|
32
|
+
});
|
|
33
|
+
const afterMount = getTestResult(renderer);
|
|
34
|
+
// Force a re-render with the same props (simulates parent re-render)
|
|
35
|
+
act(() => {
|
|
36
|
+
renderer.update(React.createElement(TestComponent, { storageKey: "test-key" }));
|
|
37
|
+
});
|
|
38
|
+
const afterUpdate = getTestResult(renderer);
|
|
39
|
+
// Render count should stay small, not grow unboundedly
|
|
40
|
+
expect(afterUpdate.renderCount).toBeLessThanOrEqual(3);
|
|
41
|
+
expect(afterUpdate.value).toEqual({ page: 0, size: 25 });
|
|
42
|
+
});
|
|
43
|
+
test("returns default value when no stored value exists", () => {
|
|
44
|
+
let renderer;
|
|
45
|
+
act(() => {
|
|
46
|
+
renderer = create(React.createElement(TestComponent, { storageKey: "missing-key" }));
|
|
47
|
+
});
|
|
48
|
+
const result = getTestResult(renderer);
|
|
49
|
+
expect(result.value).toEqual({ page: 0, size: 25 });
|
|
50
|
+
});
|
|
51
|
+
test("returns stored value when it exists", () => {
|
|
52
|
+
localStorage.setItem("stored-key", JSON.stringify({ page: 2, size: 50 }));
|
|
53
|
+
let renderer;
|
|
54
|
+
act(() => {
|
|
55
|
+
renderer = create(React.createElement(TestComponent, { storageKey: "stored-key" }));
|
|
56
|
+
});
|
|
57
|
+
const result = getTestResult(renderer);
|
|
58
|
+
expect(result.value).toEqual({ page: 2, size: 50 });
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
//# sourceMappingURL=useStorage.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useStorage.spec.js","sourceRoot":"","sources":["../../src/storage/useStorage.spec.tsx"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@variocube/app-ui",
|
|
3
|
-
"version": "1.16.
|
|
3
|
+
"version": "1.16.2",
|
|
4
4
|
"description": "Common UI components for Variocube applications.",
|
|
5
5
|
"module": "esm/index.js",
|
|
6
6
|
"types": "esm/index.d.ts",
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
"react-dom": "^17.0.2",
|
|
59
59
|
"react-router": "^6.3.0",
|
|
60
60
|
"react-router-dom": "^6.3.0",
|
|
61
|
-
"react-syntax-highlighter": "^
|
|
61
|
+
"react-syntax-highlighter": "^16.1.0",
|
|
62
62
|
"react-test-renderer": "^17.0.2",
|
|
63
63
|
"rimraf": "^3.0.2",
|
|
64
64
|
"ts-jest": "^29.1.2",
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jest-environment jsdom
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import * as React from "react";
|
|
6
|
+
import {useRef} from "react";
|
|
7
|
+
import {act, create, ReactTestRenderer} from "react-test-renderer";
|
|
8
|
+
import {useStorage} from "./useStorage";
|
|
9
|
+
|
|
10
|
+
// Reset storage between tests
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
localStorage.clear();
|
|
13
|
+
sessionStorage.clear();
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
interface TestResult<T> {
|
|
17
|
+
value: T;
|
|
18
|
+
renderCount: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function TestComponent({storageKey}: { storageKey: string }) {
|
|
22
|
+
const renderCount = useRef(0);
|
|
23
|
+
renderCount.current++;
|
|
24
|
+
|
|
25
|
+
// Inline object literal: new reference every render (the bug trigger)
|
|
26
|
+
const [value] = useStorage(storageKey, {page: 0, size: 25});
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<div
|
|
30
|
+
data-render-count={renderCount.current}
|
|
31
|
+
data-value={JSON.stringify(value)}
|
|
32
|
+
/>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function getTestResult<T>(renderer: ReactTestRenderer): TestResult<T> {
|
|
37
|
+
const div = renderer.root.findByType("div");
|
|
38
|
+
return {
|
|
39
|
+
renderCount: Number(div.props["data-render-count"]),
|
|
40
|
+
value: JSON.parse(div.props["data-value"]),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
describe("useStorage", () => {
|
|
45
|
+
test("does not cause infinite re-renders with unstable object defaultValue", () => {
|
|
46
|
+
let renderer: ReactTestRenderer;
|
|
47
|
+
act(() => {
|
|
48
|
+
renderer = create(<TestComponent storageKey="test-key" />);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const afterMount = getTestResult(renderer!);
|
|
52
|
+
|
|
53
|
+
// Force a re-render with the same props (simulates parent re-render)
|
|
54
|
+
act(() => {
|
|
55
|
+
renderer.update(<TestComponent storageKey="test-key" />);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const afterUpdate = getTestResult(renderer!);
|
|
59
|
+
|
|
60
|
+
// Render count should stay small, not grow unboundedly
|
|
61
|
+
expect(afterUpdate.renderCount).toBeLessThanOrEqual(3);
|
|
62
|
+
expect(afterUpdate.value).toEqual({page: 0, size: 25});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test("returns default value when no stored value exists", () => {
|
|
66
|
+
let renderer: ReactTestRenderer;
|
|
67
|
+
act(() => {
|
|
68
|
+
renderer = create(<TestComponent storageKey="missing-key" />);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
const result = getTestResult(renderer!);
|
|
72
|
+
expect(result.value).toEqual({page: 0, size: 25});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test("returns stored value when it exists", () => {
|
|
76
|
+
localStorage.setItem("stored-key", JSON.stringify({page: 2, size: 50}));
|
|
77
|
+
|
|
78
|
+
let renderer: ReactTestRenderer;
|
|
79
|
+
act(() => {
|
|
80
|
+
renderer = create(<TestComponent storageKey="stored-key" />);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
const result = getTestResult(renderer!);
|
|
84
|
+
expect(result.value).toEqual({page: 2, size: 50});
|
|
85
|
+
});
|
|
86
|
+
});
|
|
@@ -21,7 +21,17 @@ export function useStorage<T>(key: string, defaultValue: T, storageType?: Storag
|
|
|
21
21
|
return () => storage.removeChangeListener(key, updateStateFromStorage);
|
|
22
22
|
}, [key, updateStateFromStorage]);
|
|
23
23
|
|
|
24
|
-
const typedValue = useMemo(() =>
|
|
24
|
+
const typedValue = useMemo(() => {
|
|
25
|
+
if (value === undefined) {
|
|
26
|
+
return defaultValueSerialized !== undefined ? JSON.parse(defaultValueSerialized) : undefined;
|
|
27
|
+
}
|
|
28
|
+
try {
|
|
29
|
+
return JSON.parse(value);
|
|
30
|
+
} catch (e) {
|
|
31
|
+
console.warn(`Failed to parse storage value for key "${key}", falling back to default value.`, e);
|
|
32
|
+
return defaultValueSerialized !== undefined ? JSON.parse(defaultValueSerialized) : undefined;
|
|
33
|
+
}
|
|
34
|
+
}, [value, key, defaultValueSerialized]);
|
|
25
35
|
|
|
26
36
|
const setTypedValue = useCallback((newValue: T) => {
|
|
27
37
|
const value = JSON.stringify(newValue);
|