juststore 0.3.5 → 0.4.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/dist/form.js +31 -19
- package/dist/impl.d.ts +1 -12
- package/dist/impl.js +1 -24
- package/dist/root.js +4 -4
- package/dist/types.d.ts +18 -4
- package/package.json +1 -1
package/dist/form.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
2
|
'use client';
|
|
3
3
|
import { pascalCase } from 'change-case';
|
|
4
|
-
import { useId } from 'react';
|
|
4
|
+
import { useEffect, useId, useMemo } from 'react';
|
|
5
5
|
import { getSnapshot, produce } from './impl';
|
|
6
6
|
import { createNode } from './node';
|
|
7
7
|
import { createStoreRoot } from './root';
|
|
@@ -36,7 +36,7 @@ function useForm(defaultValue, fieldConfigs = {}) {
|
|
|
36
36
|
const errorNamespace = `errors.${namespace}`;
|
|
37
37
|
const storeApi = createStoreRoot(namespace, defaultValue, { memoryOnly: true });
|
|
38
38
|
const errorStore = createStoreRoot(errorNamespace, {}, { memoryOnly: true });
|
|
39
|
-
const formStore = {
|
|
39
|
+
const formStore = useMemo(() => ({
|
|
40
40
|
clearErrors: () => produce(errorNamespace, undefined, false, true),
|
|
41
41
|
handleSubmit: (onSubmit) => (e) => {
|
|
42
42
|
e.preventDefault();
|
|
@@ -45,8 +45,8 @@ function useForm(defaultValue, fieldConfigs = {}) {
|
|
|
45
45
|
onSubmit(getSnapshot(namespace));
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
|
-
};
|
|
49
|
-
const store = new Proxy(storeApi, {
|
|
48
|
+
}), [namespace, errorNamespace]);
|
|
49
|
+
const store = useMemo(() => new Proxy(storeApi, {
|
|
50
50
|
get(_target, prop) {
|
|
51
51
|
if (prop in formStore) {
|
|
52
52
|
return formStore[prop];
|
|
@@ -59,22 +59,34 @@ function useForm(defaultValue, fieldConfigs = {}) {
|
|
|
59
59
|
}
|
|
60
60
|
return undefined;
|
|
61
61
|
}
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
const
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
62
|
+
}), [storeApi, formStore, errorStore]);
|
|
63
|
+
const unsubscribeFns = useMemo(() => {
|
|
64
|
+
const unsubscribeFns = [];
|
|
65
|
+
for (const entry of Object.entries(fieldConfigs)) {
|
|
66
|
+
const [path, config] = entry;
|
|
67
|
+
const validator = getValidator(path, config?.validate);
|
|
68
|
+
if (validator) {
|
|
69
|
+
const unsubscribe = storeApi.subscribe(path, (value) => {
|
|
70
|
+
const error = validator(value, store);
|
|
71
|
+
if (!error) {
|
|
72
|
+
errorStore.reset(path);
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
errorStore.set(path, error);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
unsubscribeFns.push(unsubscribe);
|
|
79
|
+
}
|
|
76
80
|
}
|
|
77
|
-
|
|
81
|
+
return unsubscribeFns;
|
|
82
|
+
}, [fieldConfigs, storeApi, errorStore, store]);
|
|
83
|
+
useEffect(() => {
|
|
84
|
+
return () => {
|
|
85
|
+
for (const unsubscribe of unsubscribeFns) {
|
|
86
|
+
unsubscribe();
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
}, [unsubscribeFns]);
|
|
78
90
|
return store;
|
|
79
91
|
}
|
|
80
92
|
/**
|
package/dist/impl.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { FieldPath, FieldPathValue, FieldValues } from './path';
|
|
2
|
-
export { getNestedValue, getSnapshot, getStableKeys, isClass, isEqual, joinPath, notifyListeners, produce, rename, setExternalKeyOrder, setLeaf, subscribe, useDebounce, useObject
|
|
2
|
+
export { getNestedValue, getSnapshot, getStableKeys, isClass, isEqual, joinPath, notifyListeners, produce, rename, setExternalKeyOrder, setLeaf, subscribe, useDebounce, useObject };
|
|
3
3
|
declare function setExternalKeyOrder(target: object, keys: string[]): void;
|
|
4
4
|
declare function getStableKeys(value: unknown): string[];
|
|
5
5
|
declare function isClass(value: unknown): boolean;
|
|
@@ -97,17 +97,6 @@ declare function useObject<T extends FieldValues, P extends FieldPath<T>>(key: s
|
|
|
97
97
|
* @returns The debounced value at the path
|
|
98
98
|
*/
|
|
99
99
|
declare function useDebounce<T extends FieldValues, P extends FieldPath<T>>(key: string, path: P, delay: number): FieldPathValue<T, P> | undefined;
|
|
100
|
-
/**
|
|
101
|
-
* React hook for side effects when a value changes.
|
|
102
|
-
*
|
|
103
|
-
* Unlike `use()`, this doesn't cause re-renders. Instead, it calls the
|
|
104
|
-
* provided callback whenever the value changes, useful for syncing with
|
|
105
|
-
* external systems or triggering effects.
|
|
106
|
-
*
|
|
107
|
-
* @param key - The full key path to subscribe to
|
|
108
|
-
* @param onChange - Callback invoked with the new value on each change
|
|
109
|
-
*/
|
|
110
|
-
declare function useSubscribe<T>(key: string, onChange: (value: T) => void): void;
|
|
111
100
|
/**
|
|
112
101
|
* Sets a value at a specific path within a namespace.
|
|
113
102
|
*
|
package/dist/impl.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useEffect, useRef, useState, useSyncExternalStore } from 'react';
|
|
2
2
|
import rfcIsEqual from 'react-fast-compare';
|
|
3
3
|
import { localStorageDelete, localStorageGet, localStorageSet } from './local_storage';
|
|
4
|
-
export { getNestedValue, getSnapshot, getStableKeys, isClass, isEqual, joinPath, notifyListeners, produce, rename, setExternalKeyOrder, setLeaf, subscribe, useDebounce, useObject
|
|
4
|
+
export { getNestedValue, getSnapshot, getStableKeys, isClass, isEqual, joinPath, notifyListeners, produce, rename, setExternalKeyOrder, setLeaf, subscribe, useDebounce, useObject };
|
|
5
5
|
const memoryStore = new Map();
|
|
6
6
|
const listeners = new Map();
|
|
7
7
|
const descendantListenerKeysByPrefix = new Map();
|
|
@@ -659,29 +659,6 @@ function useDebounce(key, path, delay) {
|
|
|
659
659
|
}, [currentValue, delay, debouncedValue]);
|
|
660
660
|
return debouncedValue;
|
|
661
661
|
}
|
|
662
|
-
/**
|
|
663
|
-
* React hook for side effects when a value changes.
|
|
664
|
-
*
|
|
665
|
-
* Unlike `use()`, this doesn't cause re-renders. Instead, it calls the
|
|
666
|
-
* provided callback whenever the value changes, useful for syncing with
|
|
667
|
-
* external systems or triggering effects.
|
|
668
|
-
*
|
|
669
|
-
* @param key - The full key path to subscribe to
|
|
670
|
-
* @param onChange - Callback invoked with the new value on each change
|
|
671
|
-
*/
|
|
672
|
-
function useSubscribe(key, onChange) {
|
|
673
|
-
const onChangeRef = useRef(onChange);
|
|
674
|
-
useEffect(() => {
|
|
675
|
-
onChangeRef.current = onChange;
|
|
676
|
-
}, [onChange]);
|
|
677
|
-
useEffect(() => {
|
|
678
|
-
const unsubscribe = subscribe(key, () => {
|
|
679
|
-
const value = getSnapshot(key);
|
|
680
|
-
onChangeRef.current(value);
|
|
681
|
-
});
|
|
682
|
-
return unsubscribe;
|
|
683
|
-
}, [key]);
|
|
684
|
-
}
|
|
685
662
|
/**
|
|
686
663
|
* Sets a value at a specific path within a namespace.
|
|
687
664
|
*
|
package/dist/root.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useCallback, useRef, useSyncExternalStore } from 'react';
|
|
2
|
-
import { getNestedValue, getSnapshot, isEqual, joinPath, notifyListeners, produce, rename, setLeaf, subscribe, useDebounce, useObject
|
|
2
|
+
import { getNestedValue, getSnapshot, isEqual, joinPath, notifyListeners, produce, rename, setLeaf, subscribe, useDebounce, useObject } from './impl';
|
|
3
3
|
import { createRootNode } from './node';
|
|
4
4
|
export { createStoreRoot };
|
|
5
5
|
/**
|
|
@@ -34,9 +34,9 @@ function createStoreRoot(namespace, defaultValue, options = {}) {
|
|
|
34
34
|
value: (path) => getSnapshot(joinPath(namespace, path)),
|
|
35
35
|
reset: (path) => produce(joinPath(namespace, path), undefined, false, memoryOnly),
|
|
36
36
|
rename: (path, oldKey, newKey) => rename(joinPath(namespace, path), oldKey, newKey),
|
|
37
|
-
subscribe: (path, listener) =>
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
subscribe: (path, listener) => () => {
|
|
38
|
+
subscribe(joinPath(namespace, path), () => listener(getSnapshot(joinPath(namespace, path))));
|
|
39
|
+
},
|
|
40
40
|
useCompute: (path, fn, deps) => {
|
|
41
41
|
const fullPath = joinPath(namespace, path);
|
|
42
42
|
const fnRef = useRef(fn);
|
package/dist/types.d.ts
CHANGED
|
@@ -59,8 +59,11 @@ type StoreRoot<T extends FieldValues> = {
|
|
|
59
59
|
reset: <P extends FieldPath<T>>(path: P) => void;
|
|
60
60
|
/** Rename a key in an object. */
|
|
61
61
|
rename: <P extends FieldPath<T>>(path: P, oldKey: string, newKey: string) => void;
|
|
62
|
-
/** Subscribe to changes at path and invoke listener with the new value
|
|
63
|
-
|
|
62
|
+
/** Subscribe to changes at path and invoke listener with the new value
|
|
63
|
+
*
|
|
64
|
+
* @returns A function to unsubscribe from the path.
|
|
65
|
+
*/
|
|
66
|
+
subscribe: <P extends FieldPath<T>>(path: P, listener: (value: FieldPathValue<T, P>) => void) => () => void;
|
|
64
67
|
/** Compute a derived value from the current value, similar to useState + useMemo */
|
|
65
68
|
useCompute: <P extends FieldPath<T>, R>(path: P, fn: (value: FieldPathValue<T, P>) => R, deps?: readonly unknown[]) => R;
|
|
66
69
|
/** Notify listeners at path. */
|
|
@@ -86,8 +89,19 @@ type ValueState<T> = {
|
|
|
86
89
|
set(value: T | undefined | ((prev: T) => T), skipUpdate?: boolean): void;
|
|
87
90
|
/** Delete value at path (for arrays, removes index; for objects, deletes key). */
|
|
88
91
|
reset(): void;
|
|
89
|
-
/** Subscribe to changes at path and invoke listener with the new value.
|
|
90
|
-
|
|
92
|
+
/** Subscribe to changes at path and invoke listener with the new value.
|
|
93
|
+
*
|
|
94
|
+
* @returns A function to unsubscribe.
|
|
95
|
+
* @example
|
|
96
|
+
*
|
|
97
|
+
* useEffect(() => {
|
|
98
|
+
* const unsubscribe = store.a.b.c.subscribe(value => {
|
|
99
|
+
* console.log(value)
|
|
100
|
+
* })
|
|
101
|
+
* return unsubscribe
|
|
102
|
+
* }, [])
|
|
103
|
+
*/
|
|
104
|
+
subscribe(listener: (value: T) => void): () => void;
|
|
91
105
|
/** Compute a derived value from the current value, similar to useState + useMemo */
|
|
92
106
|
useCompute: <R>(fn: (value: T) => R, deps?: readonly unknown[]) => R;
|
|
93
107
|
/** Ensure the value is an array. */
|