~repos /atoms-state

#js#react#flux

git clone https://pyrossh.dev/repos/atoms-state.git

Simple State management for react


316fb6e7 Peter John

3 years ago
add watchOn capability
Files changed (3) hide show
  1. index.d.ts +10 -1
  2. index.js +58 -25
  3. 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: { get: () => T; set: (v: T) => void; clear: () => void }) => void;
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
- setStoredValue({ ...initialValue, loading: false });
188
+ setData({ ...initialValue, loading: false });
189
189
  }
190
190
  })
191
- .catch(() => setStoredValue({ ...storedValue, loading: false }));
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 fields = {};
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
- fields[name] = {
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
- delete fields[name];
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 Object.keys(fields)) {
254
+ for (const name of Fields.keys()) {
233
- obj[name] = fields[name].get();
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 Object.keys(fields)) {
261
+ for (const name of Fields.keys()) {
240
- fields[name].set(dotData[name]);
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 Object.keys(fields)) {
287
+ for (const name of Fields.keys()) {
255
- fields[name].clear();
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 = fields[name];
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 = fields[name];
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 = fields[name];
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 = fields[name];
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 = fields[name];
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 Object.keys(fields)) {
329
+ for (const name of Fields.keys()) {
297
- fields[name].setError('');
330
+ Fields.get(name).setError('');
298
331
  }
299
- const allValid = Object.keys(fields)
332
+ const allValid = Fields.keys()
300
- .map((key) => fields[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 = fields[k];
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.7.3",
5
+ "version": "0.8.0",
6
6
  "license": "MIT",
7
7
  "type": "module",
8
8
  "main": "index.js",