~repos /atoms-state
git clone https://pyrossh.dev/repos/atoms-state.git
Simple State management for react
316fb6e7
—
Peter John 3 years ago
add watchOn capability
- index.d.ts +10 -1
- index.js +58 -25
- package.json +1 -1
index.d.ts
CHANGED
|
@@ -18,16 +18,25 @@ export declare const useAsyncAtom: <P, S>(a: AsyncAtom<P, S>, params: P) => S;
|
|
|
18
18
|
declare type PromiseFunc<S, P> = (p: P) => Promise<S>;
|
|
19
19
|
export declare const usePromise: <S, P>(fn: PromiseFunc<S, P>, params: P) => [S, (v: S) => void];
|
|
20
20
|
|
|
21
|
+
export type Field<T> = {
|
|
22
|
+
get: () => T;
|
|
23
|
+
set: (v: T) => void;
|
|
24
|
+
clear: () => void;
|
|
25
|
+
watch?: () => void;
|
|
26
|
+
};
|
|
27
|
+
|
|
21
28
|
export function useField<T>(name: string): {
|
|
22
29
|
name: string;
|
|
23
30
|
error: string;
|
|
24
31
|
setError: (v: string | undefined) => void;
|
|
32
|
+
getField: () => Field<T>;
|
|
25
|
-
registerField: (f:
|
|
33
|
+
registerField: (f: Field<T>) => void;
|
|
26
34
|
};
|
|
27
35
|
|
|
28
36
|
export function useForm<T>(
|
|
29
37
|
initial: T,
|
|
30
38
|
submit: (d: T) => Promise<void>,
|
|
39
|
+
watchOn?: Array<string>,
|
|
31
40
|
): {
|
|
32
41
|
error: string;
|
|
33
42
|
setError: (v?: string) => void;
|
index.js
CHANGED
|
@@ -183,12 +183,13 @@ export const useAsyncStorage = (key, initialValue) => {
|
|
|
183
183
|
Storage.getItem(key)
|
|
184
184
|
.then((v) => {
|
|
185
185
|
if (v) {
|
|
186
|
-
setData(v);
|
|
186
|
+
setData({ ...v, loading: false });
|
|
187
187
|
} else {
|
|
188
|
-
|
|
188
|
+
setData({ ...initialValue, loading: false });
|
|
189
189
|
}
|
|
190
190
|
})
|
|
191
|
-
.catch(() => setStoredValue({ ...
|
|
191
|
+
.catch((err) => setStoredValue({ ...initialValue, loading: false, err }));
|
|
192
|
+
|
|
192
193
|
return Storage.subscribe(key, (v) => {
|
|
193
194
|
setStoredValue(v);
|
|
194
195
|
});
|
|
@@ -196,7 +197,21 @@ export const useAsyncStorage = (key, initialValue) => {
|
|
|
196
197
|
return [storedValue, setData];
|
|
197
198
|
};
|
|
198
199
|
|
|
199
|
-
export const
|
|
200
|
+
export const Fields = {
|
|
201
|
+
data: {},
|
|
202
|
+
keys() {
|
|
203
|
+
return Object.keys(this.data);
|
|
204
|
+
},
|
|
205
|
+
get(k) {
|
|
206
|
+
return this.data[k];
|
|
207
|
+
},
|
|
208
|
+
set(k, v) {
|
|
209
|
+
this.data[k] = v;
|
|
210
|
+
},
|
|
211
|
+
remove(k) {
|
|
212
|
+
delete this.data[k];
|
|
213
|
+
},
|
|
214
|
+
};
|
|
200
215
|
|
|
201
216
|
export const useField = (name) => {
|
|
202
217
|
const [error, setError] = useState('');
|
|
@@ -212,79 +227,97 @@ export const useField = (name) => {
|
|
|
212
227
|
name,
|
|
213
228
|
error,
|
|
214
229
|
setError: setErrorMessage,
|
|
230
|
+
getField: () => Fields.get(name),
|
|
215
231
|
registerField: (f) => {
|
|
232
|
+
const oldField = Fields.get(name) || {};
|
|
216
|
-
|
|
233
|
+
Fields.set(name, {
|
|
234
|
+
...oldField,
|
|
217
235
|
...f,
|
|
218
236
|
getError: () => errorRef.current,
|
|
219
237
|
setError: setErrorMessage,
|
|
220
|
-
};
|
|
238
|
+
});
|
|
221
239
|
return () => {
|
|
240
|
+
// TODO: this remove happens after refresh so that state persists for 1 render
|
|
241
|
+
// could cause wrong calcuations later on
|
|
222
|
-
|
|
242
|
+
Fields.remove(name);
|
|
223
243
|
};
|
|
224
244
|
},
|
|
225
245
|
};
|
|
226
246
|
};
|
|
227
247
|
|
|
228
|
-
export const useForm = (initial, submit) => {
|
|
248
|
+
export const useForm = (initial, submit, watchOn = []) => {
|
|
229
249
|
const [error, setError] = useState('');
|
|
250
|
+
const [, rerender] = useState(false);
|
|
251
|
+
const refresh = () => rerender((v) => !v);
|
|
230
252
|
const getData = () => {
|
|
231
253
|
const obj = {};
|
|
232
|
-
for (const name of
|
|
254
|
+
for (const name of Fields.keys()) {
|
|
233
|
-
obj[name] =
|
|
255
|
+
obj[name] = Fields.get(name).get();
|
|
234
256
|
}
|
|
235
257
|
return dot.object(obj);
|
|
236
258
|
};
|
|
237
259
|
const setData = useCallback((data) => {
|
|
238
260
|
const dotData = dot.dot(data);
|
|
239
|
-
for (const name of
|
|
261
|
+
for (const name of Fields.keys()) {
|
|
240
|
-
|
|
262
|
+
Fields.get(name).set(dotData[name]);
|
|
241
263
|
}
|
|
242
264
|
}, []);
|
|
243
265
|
useEffect(() => {
|
|
244
266
|
if (initial) {
|
|
245
267
|
setData(initial);
|
|
246
268
|
}
|
|
247
|
-
}, [initial, setData]);
|
|
269
|
+
}, [JSON.stringify(initial), setData]);
|
|
270
|
+
useEffect(() => {
|
|
271
|
+
for (const name of watchOn) {
|
|
272
|
+
const field = Fields.get(name);
|
|
273
|
+
if (field) {
|
|
274
|
+
Fields.set(name, {
|
|
275
|
+
...field,
|
|
276
|
+
watch: refresh,
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}, [Fields.keys().length]);
|
|
248
281
|
return {
|
|
249
282
|
error,
|
|
250
283
|
setError,
|
|
251
284
|
getData,
|
|
252
285
|
setData,
|
|
253
286
|
clear: () => {
|
|
254
|
-
for (const name of
|
|
287
|
+
for (const name of Fields.keys()) {
|
|
255
|
-
|
|
288
|
+
Fields.get(name).clear();
|
|
256
289
|
}
|
|
257
290
|
},
|
|
258
291
|
reset: () => {
|
|
259
292
|
setData(initial);
|
|
260
293
|
},
|
|
261
294
|
getFieldValue: (name) => {
|
|
262
|
-
const field =
|
|
295
|
+
const field = Fields.get(name);
|
|
263
296
|
if (field) {
|
|
264
297
|
return field.get();
|
|
265
298
|
}
|
|
266
299
|
},
|
|
267
300
|
setFieldValue: (name, value) => {
|
|
268
|
-
const field =
|
|
301
|
+
const field = Fields.get(name);
|
|
269
302
|
if (field) {
|
|
270
303
|
field.set(value);
|
|
271
304
|
}
|
|
272
305
|
},
|
|
273
306
|
clearFieldValue: (name) => {
|
|
274
|
-
const field =
|
|
307
|
+
const field = Fields.get(name);
|
|
275
308
|
if (field) {
|
|
276
309
|
field.clear();
|
|
277
310
|
}
|
|
278
311
|
},
|
|
279
312
|
getFieldError: (name) => {
|
|
280
|
-
const field =
|
|
313
|
+
const field = Fields.get(name);
|
|
281
314
|
if (field) {
|
|
282
315
|
return field.getError();
|
|
283
316
|
}
|
|
284
317
|
return '';
|
|
285
318
|
},
|
|
286
319
|
setFieldError: (name, value) => {
|
|
287
|
-
const field =
|
|
320
|
+
const field = Fields.get(name);
|
|
288
321
|
if (field) {
|
|
289
322
|
field.setError(value);
|
|
290
323
|
}
|
|
@@ -293,11 +326,11 @@ export const useForm = (initial, submit) => {
|
|
|
293
326
|
try {
|
|
294
327
|
const data = getData();
|
|
295
328
|
setError('');
|
|
296
|
-
for (const name of
|
|
329
|
+
for (const name of Fields.keys()) {
|
|
297
|
-
|
|
330
|
+
Fields.get(name).setError('');
|
|
298
331
|
}
|
|
299
|
-
const allValid =
|
|
332
|
+
const allValid = Fields.keys()
|
|
300
|
-
.map((key) =>
|
|
333
|
+
.map((key) => Fields.get(key))
|
|
301
334
|
.reduce((acc, field) => acc && field.getError() === '', true);
|
|
302
335
|
if (allValid) {
|
|
303
336
|
await submit(data);
|
|
@@ -306,7 +339,7 @@ export const useForm = (initial, submit) => {
|
|
|
306
339
|
// this is for validation errors
|
|
307
340
|
if (err.error && typeof err.error !== 'string') {
|
|
308
341
|
Object.keys(err.error).forEach((k) => {
|
|
309
|
-
const field =
|
|
342
|
+
const field = Fields.get(k);
|
|
310
343
|
if (field) {
|
|
311
344
|
field.setError(err.error[k]);
|
|
312
345
|
}
|
package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@pyros2097/atoms-state",
|
|
3
3
|
"author": "pyros.sh",
|
|
4
4
|
"description": "State management and common hooks",
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.8.0",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"main": "index.js",
|