juststore 0.3.2 → 0.3.3
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/dist/root.js +24 -16
- package/package.json +9 -9
package/dist/root.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useCallback, useRef, useSyncExternalStore } from 'react';
|
|
2
|
-
import { getNestedValue, getSnapshot, joinPath, notifyListeners, produce, rename, setLeaf, subscribe, useDebounce, useObject, useSubscribe } from './impl';
|
|
2
|
+
import { getNestedValue, getSnapshot, isEqual, joinPath, notifyListeners, produce, rename, setLeaf, subscribe, useDebounce, useObject, useSubscribe } from './impl';
|
|
3
3
|
import { createRootNode } from './node';
|
|
4
4
|
export { createStoreRoot };
|
|
5
5
|
/**
|
|
@@ -41,19 +41,27 @@ function createStoreRoot(namespace, defaultValue, options = {}) {
|
|
|
41
41
|
const fullPath = joinPath(namespace, path);
|
|
42
42
|
const fnRef = useRef(fn);
|
|
43
43
|
fnRef.current = fn;
|
|
44
|
-
// Cache to avoid infinite loops - only recompute when store value changes
|
|
45
44
|
const cacheRef = useRef(null);
|
|
46
45
|
const subscribeToPath = useCallback((onStoreChange) => subscribe(fullPath, onStoreChange), [fullPath]);
|
|
47
46
|
const getComputedSnapshot = useCallback(() => {
|
|
47
|
+
if (cacheRef.current && cacheRef.current.path !== fullPath) {
|
|
48
|
+
cacheRef.current = null;
|
|
49
|
+
}
|
|
48
50
|
const storeValue = getSnapshot(fullPath);
|
|
49
|
-
|
|
50
|
-
|
|
51
|
+
if (cacheRef.current && isEqual(cacheRef.current.storeValue, storeValue)) {
|
|
52
|
+
// same store value, return the same computed value
|
|
53
|
+
return cacheRef.current.computed;
|
|
54
|
+
}
|
|
55
|
+
const computedNext = fnRef.current(storeValue);
|
|
56
|
+
// Important: even if storeValue changed, we should avoid forcing a re-render
|
|
57
|
+
// when the computed result is logically unchanged. `useSyncExternalStore`
|
|
58
|
+
// uses `Object.is` on the snapshot; returning the same reference will bail out.
|
|
59
|
+
if (cacheRef.current && isEqual(cacheRef.current.computed, computedNext)) {
|
|
60
|
+
cacheRef.current.storeValue = storeValue;
|
|
51
61
|
return cacheRef.current.computed;
|
|
52
62
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
cacheRef.current = { storeValue, computed };
|
|
56
|
-
return computed;
|
|
63
|
+
cacheRef.current = { path: fullPath, storeValue, computed: computedNext };
|
|
64
|
+
return computedNext;
|
|
57
65
|
}, [fullPath]);
|
|
58
66
|
return useSyncExternalStore(subscribeToPath, getComputedSnapshot, getComputedSnapshot);
|
|
59
67
|
},
|
|
@@ -66,28 +74,28 @@ function createStoreRoot(namespace, defaultValue, options = {}) {
|
|
|
66
74
|
});
|
|
67
75
|
},
|
|
68
76
|
useState: (path) => {
|
|
69
|
-
const
|
|
77
|
+
const fullPath = joinPath(namespace, path);
|
|
70
78
|
const setValue = useCallback((value) => {
|
|
71
79
|
if (typeof value === 'function') {
|
|
72
|
-
const currentValue = getSnapshot(
|
|
80
|
+
const currentValue = getSnapshot(fullPath);
|
|
73
81
|
const newValue = value(currentValue);
|
|
74
82
|
return setLeaf(namespace, path, newValue, false, memoryOnly);
|
|
75
83
|
}
|
|
76
84
|
return setLeaf(namespace, path, value, false, memoryOnly);
|
|
77
|
-
}, [path]);
|
|
78
|
-
return [useObject(
|
|
85
|
+
}, [fullPath, path]);
|
|
86
|
+
return [useObject(namespace, path), setValue];
|
|
79
87
|
},
|
|
80
88
|
Render: ({ path, children }) => {
|
|
81
|
-
const
|
|
82
|
-
const value = useObject(
|
|
89
|
+
const fullPath = joinPath(namespace, path);
|
|
90
|
+
const value = useObject(namespace, path);
|
|
83
91
|
const update = useCallback((value) => {
|
|
84
92
|
if (typeof value === 'function') {
|
|
85
|
-
const currentValue = getSnapshot(
|
|
93
|
+
const currentValue = getSnapshot(fullPath);
|
|
86
94
|
const newValue = value(currentValue);
|
|
87
95
|
return setLeaf(namespace, path, newValue, false, memoryOnly);
|
|
88
96
|
}
|
|
89
97
|
return setLeaf(namespace, path, value, false, memoryOnly);
|
|
90
|
-
}, [path]);
|
|
98
|
+
}, [fullPath, path]);
|
|
91
99
|
return children(value, update);
|
|
92
100
|
},
|
|
93
101
|
Show: ({ path, children, on }) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "juststore",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.3",
|
|
4
4
|
"description": "A small, expressive, and type-safe state management library for React.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -52,19 +52,19 @@
|
|
|
52
52
|
"react-fast-compare": "^3.2.2"
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
|
-
"@eslint/js": "^9.39.
|
|
56
|
-
"@types/node": "^24.10.
|
|
57
|
-
"@types/react": "^19.2.
|
|
58
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
59
|
-
"@typescript-eslint/parser": "^8.
|
|
60
|
-
"eslint": "^9.39.
|
|
55
|
+
"@eslint/js": "^9.39.2",
|
|
56
|
+
"@types/node": "^24.10.4",
|
|
57
|
+
"@types/react": "^19.2.7",
|
|
58
|
+
"@typescript-eslint/eslint-plugin": "^8.50.1",
|
|
59
|
+
"@typescript-eslint/parser": "^8.50.1",
|
|
60
|
+
"eslint": "^9.39.2",
|
|
61
61
|
"eslint-plugin-prettier": "^5.5.4",
|
|
62
62
|
"eslint-plugin-react": "^7.37.5",
|
|
63
63
|
"eslint-plugin-react-hooks": "^7.0.1",
|
|
64
|
-
"eslint-plugin-react-refresh": "^0.4.
|
|
64
|
+
"eslint-plugin-react-refresh": "^0.4.26",
|
|
65
65
|
"husky": "^9.1.7",
|
|
66
66
|
"prettier": "^3.7.4",
|
|
67
|
-
"react": "^19.2.
|
|
67
|
+
"react": "^19.2.3",
|
|
68
68
|
"typescript": "^5.9.3"
|
|
69
69
|
}
|
|
70
70
|
}
|