gform-react 2.7.5 → 2.8.0

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.
Files changed (41) hide show
  1. package/README.md +30 -30
  2. package/dist/cjs/gform-react.development.js +165 -140
  3. package/dist/cjs/gform-react.development.js.map +1 -1
  4. package/dist/cjs/gform-react.production.js +1 -1
  5. package/dist/cjs/gform-react.production.js.map +1 -1
  6. package/dist/esm/GForm.development.js +12 -9
  7. package/dist/esm/GForm.development.js.map +1 -1
  8. package/dist/esm/GForm.production.js +1 -1
  9. package/dist/esm/GForm.production.js.map +1 -1
  10. package/dist/esm/GInput.development.js +22 -21
  11. package/dist/esm/GInput.development.js.map +1 -1
  12. package/dist/esm/GInput.production.js +1 -1
  13. package/dist/esm/GInput.production.js.map +1 -1
  14. package/dist/esm/GValidator.development.js +5 -1
  15. package/dist/esm/GValidator.development.js.map +1 -1
  16. package/dist/esm/GValidator.production.js +1 -1
  17. package/dist/esm/GValidator.production.js.map +1 -1
  18. package/dist/esm/shared.development.js +133 -112
  19. package/dist/esm/shared.development.js.map +1 -1
  20. package/dist/esm/shared.production.js +1 -1
  21. package/dist/esm/shared.production.js.map +1 -1
  22. package/dist/esm/useFormSelector.development.js +3 -2
  23. package/dist/esm/useFormSelector.development.js.map +1 -1
  24. package/dist/esm/useFormSelector.production.js +1 -1
  25. package/native/dist/cjs/gform-react.development.js +151 -133
  26. package/native/dist/cjs/gform-react.development.js.map +1 -1
  27. package/native/dist/cjs/gform-react.production.js +1 -1
  28. package/native/dist/cjs/gform-react.production.js.map +1 -1
  29. package/native/dist/esm/RNGForm.development.js +4 -4
  30. package/native/dist/esm/RNGForm.development.js.map +1 -1
  31. package/native/dist/esm/RNGForm.production.js +1 -1
  32. package/native/dist/esm/RNGForm.production.js.map +1 -1
  33. package/native/dist/esm/RNGInput.development.js +23 -21
  34. package/native/dist/esm/RNGInput.development.js.map +1 -1
  35. package/native/dist/esm/RNGInput.production.js +1 -1
  36. package/native/dist/esm/RNGInput.production.js.map +1 -1
  37. package/native/dist/esm/shared.development.js +133 -112
  38. package/native/dist/esm/shared.development.js.map +1 -1
  39. package/native/dist/esm/shared.production.js +1 -1
  40. package/native/dist/esm/shared.production.js.map +1 -1
  41. package/package.json +3 -3
package/README.md CHANGED
@@ -9,39 +9,45 @@
9
9
  ## Features
10
10
 
11
11
  - **Lightweight**
12
- - **Tree‑shakeable**
13
- - **Minimal re-renders** — only updates fields that change
14
- - **Native HTML Constraint Validations**
15
- - **Custom Validations**
16
- - **Custom Async Validations**
17
- - **Supports Yup, Zod, and more**
18
- - **Dynamic forms** — add/remove fields on the fly
19
- - **Accessibility‑friendly**
20
- Automatically sets `aria-required` and `aria-invalid`
21
- - **React Native support**
22
-
12
+ - **Tree‑shakable** — import only what you use to keep bundles small
13
+ - **Minimal re-renders** — updates only the fields that actually change
14
+ - **Native HTML constraint validation** — full support for `min`, `max`, `pattern`, `minLength`, `maxLength`, `required`, and more
15
+ - **Custom Validations** – add custom validation with any rules
16
+ - **Async Validations** — run asynchronous rules for server-side checks
17
+ - **Supports Yup, Zod, and more** – use any validation library you like
18
+ - **Deeply Nested Forms** — structure forms however you like, across any number of components
19
+ - **Dynamic fields** — add or remove fields at runtime without losing state
20
+ - **Native `<form>` actions** — fully supports browser‑level form submission, including action, method, and HTTP navigation, with no JavaScript required
21
+ - **Next.js Server Actions support** — works seamlessly with Server Actions through standard `<form>` submissions, with no special adapters or client‑side wiring
22
+ - **Accessibility‑friendly** — automatically manages `aria-required` and `aria-invalid`
23
+ - **React Native support** — works seamlessly across web and mobile
24
+
23
25
  <br/>
24
26
  <div align="center">
25
- <a href="https://bundlephobia.com/package/gform-react">
26
- <img src="https://img.shields.io/bundlephobia/min/gform-react?label=minified%20size&color=darkergreen" alt="Minified size">
27
- </a>
28
-
29
- <a href="https://bundlephobia.com/package/gform-react">
30
- <img src="https://img.shields.io/bundlephobia/minzip/gform-react?label=gzip%20size&color=darkergreen" alt="Gzip size">
27
+ <a href="https://unpkg.com/gform-react@latest/dist/cjs/gform-react.production.js">
28
+ <img src="http://img.badgesize.io/https://unpkg.com/gform-react@latest/dist/cjs/gform-react.production.js?compression=gzip&style=for-the-badge" alt="Minified size">
31
29
  </a>
32
30
 
33
- <img src="https://img.shields.io/npm/dm/gform-react" alt="npm downloads">
34
-
35
- <img src="https://img.shields.io/npm/dependency-version/gform-react/peer/react" alt="React peer dependency">
36
-
37
- <img src="https://img.shields.io/npm/dependency-version/gform-react/peer/react-dom" alt="React DOM peer dependency">
38
-
31
+ <img src="https://img.shields.io/npm/dt/gform-react.svg?style=for-the-badge" alt="React DOM peer dependency">
32
+ <img src="https://img.shields.io/npm/dm/gform-react?style=for-the-badge" alt="npm downloads">
33
+ </div>
34
+ <div align="center">
35
+ <img src="https://img.shields.io/npm/dependency-version/gform-react/peer/react?style=for-the-badge" alt="React peer dependency">
36
+
37
+ <img src="https://img.shields.io/npm/dependency-version/gform-react/peer/react-dom?style=for-the-badge" alt="React DOM peer dependency">
38
+
39
39
  <a href="https://unpkg.com/gform-react@latest/LICENSE.md">
40
- <img src="https://img.shields.io/npm/l/gform-react" alt="MIT License">
40
+ <img src="https://img.shields.io/npm/l/gform-react?style=for-the-badge" alt="MIT License">
41
41
  </a>
42
42
  </div>
43
43
  <br/>
44
44
 
45
+ ## Documentation
46
+
47
+ Full documentation, examples, and API reference:
48
+
49
+ https://gform-react.onrender.com
50
+
45
51
  ## QuickStart
46
52
  ```tsx
47
53
  import {GForm, GInput, GValidator, type GValidators} from "gform-react";
@@ -90,12 +96,6 @@ const App: FC = () => {
90
96
  };
91
97
  ```
92
98
 
93
- ## Documentation
94
-
95
- Full documentation, examples, and API reference:
96
-
97
- https://gform-react.onrender.com
98
-
99
99
  ## Installation
100
100
 
101
101
  npm:
@@ -20,87 +20,52 @@ const typeValueDict = {
20
20
  number: 'valueAsNumber'
21
21
  };
22
22
  const _generateIdUnsafe = () => (+new Date()).toString(36) + (1 - Math.random()).toString(36).substring(2, 16);
23
- const _copyStateFields = (source, destination) => {
24
- for (const key in destination.fields) {
25
- const sourceField = source.fields[key];
26
- const destField = destination.fields[key];
27
- if (!sourceField || sourceField.type !== destField.type) continue;
28
- destination.fields[key] = {
29
- ...destField,
30
- ...sourceField
31
- };
32
- }
33
- };
34
- const _buildFormInitialValues = (rows = []) => {
35
- const fields = {};
36
- const inputs = _findInputs(rows);
37
- inputs.forEach(config => {
38
- if (fields[config.formKey]) {
39
- console.warn(`DEV ONLY - [Duplicate Keys] - field with key '${config.formKey}' already defined.`);
40
- }
41
- const {
42
- required = false,
43
- max,
44
- maxLength,
45
- min,
46
- minLength,
47
- step,
48
- pattern,
49
- type = "text",
50
- defaultValue,
51
- value,
52
- checked,
53
- defaultChecked,
54
- formKey,
55
- debounce,
56
- validatorKey
57
- } = config;
58
- const defaultProps = defaultFieldProps[type] || defaultFieldProps.text;
59
- const inputValue = value || defaultValue || checked || defaultChecked || defaultProps.value;
60
- fields[formKey] = {
61
- formKey,
62
- type,
63
- required,
64
- max,
65
- maxLength,
66
- min,
67
- minLength,
68
- step,
69
- pattern,
70
- value: inputValue,
71
- validatorKey,
72
- debounce,
73
- dirty: false,
74
- touched: false,
75
- gid: _generateIdUnsafe()
76
- };
77
- });
23
+ const _buildInputInitialValues = input => {
24
+ const {
25
+ required = false,
26
+ max,
27
+ maxLength,
28
+ min,
29
+ minLength,
30
+ step,
31
+ pattern,
32
+ type = "text",
33
+ defaultValue,
34
+ value,
35
+ checked,
36
+ defaultChecked,
37
+ formKey,
38
+ debounce,
39
+ validatorKey
40
+ } = input;
41
+ const defaultProps = defaultFieldProps[type] || defaultFieldProps.text;
42
+ const inputValue = value || defaultValue || checked || defaultChecked || defaultProps.value;
78
43
  return {
79
- fields: fields
80
- };
81
- };
82
- const _findInputs = (root, total = []) => {
83
- if (!root) return total;
84
- React.Children.forEach(root, child => {
85
- if (!React.isValidElement(child)) return;
86
- if (child.props) {
87
- const {
88
- formKey,
89
- children
90
- } = child.props;
91
- if (formKey) {
92
- total.push(child.props);
93
- }
94
- if (children) {
95
- _findInputs(children, total);
96
- }
44
+ formKey,
45
+ type,
46
+ required,
47
+ max,
48
+ maxLength,
49
+ min,
50
+ minLength,
51
+ step,
52
+ pattern,
53
+ value: inputValue,
54
+ validatorKey,
55
+ debounce,
56
+ dirty: false,
57
+ touched: false,
58
+ gid: _generateIdUnsafe(),
59
+ error: false,
60
+ errorText: '',
61
+ dispatchChanges(changes) {},
62
+ checkValidity() {
63
+ return false;
97
64
  }
98
- });
99
- return total;
65
+ };
100
66
  };
101
- const _findValidityKey = (validity, exclude = []) => {
67
+ const _findValidityKey = validity => {
102
68
  for (const key in validity) {
103
- if (exclude.includes(key)) continue;
104
69
  if (key !== 'valid' && validity[key]) {
105
70
  return key;
106
71
  }
@@ -350,7 +315,7 @@ class GValidator {
350
315
  }
351
316
  this.track.push(validityKey);
352
317
  }
353
- this._constraintHandlers.push((input, key) => {
318
+ const constraintHandler = (input, key) => {
354
319
  {
355
320
  if (validityKey && validityMap[validityKey] && typeof input[validityMap[validityKey]] === 'undefined') {
356
321
  console.warn(`DEV ONLY - [Missing Prop] - the input '${input.formKey}' has registered validator for the violation '${validityKey}' but the input hasn't described the constraint '${validityMap[validityKey]}'.\nadd '${validityMap[validityKey]}' to the input props.\nexample:\n<GInput formKey='${input.formKey}' ${validityMap[validityKey]}={...} />\n\nor either remove '.${handlersMap[validityMap[validityKey]]}(...)' validation`);
@@ -361,7 +326,11 @@ class GValidator {
361
326
  return true;
362
327
  }
363
328
  return false;
329
+ };
330
+ Object.defineProperty(constraintHandler, 'name', {
331
+ value: `constraintHandler_${validityKey}`
364
332
  });
333
+ this._constraintHandlers.push(constraintHandler);
365
334
  return this;
366
335
  }
367
336
  }
@@ -370,30 +339,32 @@ const useFormHandlers = (getState, setState, validators = {}, optimized = false)
370
339
  const _viHandler = (input, e) => {
371
340
  if (!input) return;
372
341
  const element = e && e.target;
342
+ input.touched = true;
373
343
  if (typeof document !== 'undefined' && (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement || element instanceof HTMLSelectElement)) {
374
344
  if (!input.checkValidity) input.checkValidity = () => element.checkValidity();
375
- const hasInitialValue = !input.dirty && input.value && !input.touched;
345
+ const hasInitialValue = !input.dirty && input.value;
376
346
  if (hasInitialValue) {
377
- _checkInputManually(input);
347
+ const {
348
+ validityKey = 'custom'
349
+ } = _checkInputManually(input);
350
+ element.setCustomValidity(validityKey);
378
351
  _dispatchChanges(input, input.formKey);
379
352
  return;
380
353
  }
381
354
  element.setCustomValidity('');
382
- const exclude = input.type && (input.pattern || hasCustomValidation(input)) ? ['typeMismatch'] : [];
383
- const validityKey = _findValidityKey(element.validity, exclude);
355
+ const validityKey = _findValidityKey(element.validity);
384
356
  _validateInput(input, validityKey, v => element.setCustomValidity(v));
385
357
  if (!validityKey && input.error) {
386
358
  element.setCustomValidity(input.errorText || 'error');
387
359
  }
388
360
  _dispatchChanges(input, input.formKey);
389
361
  } else {
390
- input.checkValidity = () => _checkInputManually(input);
362
+ input.checkValidity = () => _checkInputManually(input).isValid;
391
363
  input.checkValidity();
392
364
  _dispatchChanges(input, input.formKey);
393
365
  }
394
366
  };
395
367
  const _checkInputManually = input => {
396
- const exclude = input.type && (input.pattern || hasCustomValidation(input)) ? ['typeMismatch'] : [];
397
368
  let validityKey = _findValidityKey({
398
369
  valueMissing: input.required && !input.value || false,
399
370
  typeMismatch: _checkTypeMismatch(input),
@@ -402,28 +373,34 @@ const useFormHandlers = (getState, setState, validators = {}, optimized = false)
402
373
  patternMismatch: input.pattern && _checkResult(input.pattern, input.value) || false,
403
374
  rangeUnderflow: input.min && Number(input.value) < Number(input.min) || false,
404
375
  rangeOverflow: input.max && Number(input.value) > Number(input.max) || false
405
- }, exclude);
376
+ });
406
377
  if (!validityKey && input.error) {
407
378
  validityKey = 'customError';
408
379
  }
409
380
  _validateInput(input, validityKey);
410
- return !input.error;
381
+ return {
382
+ isValid: !input.error,
383
+ validityKey
384
+ };
411
385
  };
412
386
  const _updateInputHandler = (input, e, unknown) => {
413
387
  input.value = _extractValue(e, unknown);
388
+ input.dirty = true;
414
389
  _viHandler(input, e);
415
390
  };
416
391
  const _validateInput = (input, validityKey, setValidity) => {
417
392
  const inputValidator = validators[input.validatorKey || input.formKey] || validators['*'];
418
393
  {
419
394
  if (validityKey && !inputValidator?.hasConstraint(validityKey)) {
420
- if (validityKey === 'typeMismatch') console.warn(`DEV ONLY - [Missing Validator] - the input '${input.formKey}' has described the constraint '${validityMap[validityKey]}' however, a correspond validator/custom validation/pattern validator is missing.\nadd '${handlersMap[validityMap[validityKey]]}' or 'withCustomValidation' or '${handlersMap[validityMap.patternMismatch]}' to the input validator.\nexample:\nconst validators: GValidators = {\n\temail: new GValidator().withPatternMismatchMessage('pattern mismatch'),\n\t...\n}\n\nor either remove the constraint '${validityMap[validityKey]}' from the input props`);else console.warn(`DEV ONLY - [Missing Validator] - the input '${input.formKey}' has described the constraint '${validityMap[validityKey]}' however, a correspond validator is missing.\nadd '${handlersMap[validityMap[validityKey]]}' to the input validator.\nexample:\nconst validators: GValidators = {\n\temail: new GValidator().withPatternMismatchMessage('pattern mismatch'),\n\t...\n}\n\nor either remove the constraint '${validityMap[validityKey]}' from the input props`);
395
+ if (validityKey === 'typeMismatch') {
396
+ if (!inputValidator?.handlers.length) console.warn(`DEV ONLY - [Missing Validator] - the input '${input.formKey}' has described the constraint '${validityMap[validityKey]}' however, a correspond validator is missing.\nadd '${handlersMap[validityMap[validityKey]]}' or 'withCustomValidation' or '${handlersMap[validityMap.patternMismatch]}' to the input validator.\nexample:\nconst validators: GValidators = {\n\temail: new GValidator().withTypeMismatchMessage('pattern mismatch'),\n\t...\n}\nif you added on of these validators then the input is still suffering from '${validityKey}' violation.\n\nor either remove the constraint '${validityMap[validityKey]}' from the input props.\n`);else console.warn(`DEV ONLY - [Missing Validator] - the input '${input.formKey}' has described the constraint '${validityMap[validityKey]}' however, a correspond validator is missing or not satisfies the native constraint.\nadd '${handlersMap[validityMap[validityKey]]}' or 'withCustomValidation' to the input validator.\nexample:\nconst validators: GValidators = {\n\temail: new GValidator().withTypeMismatchMessage('pattern mismatch'),\n\t...\n}\n\nif you already have a Custom Validation then the input is still not satisfies the native type pattern.\neither enforce it or remove the constraint '${validityMap[validityKey]}' from the input props`);
397
+ } else console.warn(`DEV ONLY - [Missing Validator] - the input '${input.formKey}' has described the constraint '${validityMap[validityKey]}' however, a correspond validator is missing.\nadd '${handlersMap[validityMap[validityKey]]}' to the input validator.\nexample:\nconst validators: GValidators = {\n\temail: new GValidator().withPatternMismatchMessage('pattern mismatch'),\n\t...\n}\n\nor either remove the constraint '${validityMap[validityKey]}' from the input props`);
398
+ console.warn(`form submition is prevented due to violation(s) of input '${input.formKey}': violation '${validityKey}' caused by '${validityMap[validityKey]}' property (<Ginput ${validityMap[validityKey]}={...} />)`);
421
399
  }
422
400
  }
423
401
  if (inputValidator) {
424
402
  __validateInput(input, inputValidator, validityKey, setValidity);
425
403
  }
426
- input.touched = true;
427
404
  };
428
405
  const _dispatchChanges = (changes, key) => setState(prev => {
429
406
  if (key) {
@@ -446,12 +423,14 @@ const useFormHandlers = (getState, setState, validators = {}, optimized = false)
446
423
  const __validateInput = (input, inputValidator, validityKey, setValidity) => {
447
424
  const fields = getState().fields;
448
425
  for (const index in inputValidator.constraintHandlers) {
449
- const result = inputValidator.constraintHandlers[index](input, validityKey);
426
+ const handler = inputValidator.constraintHandlers[index];
427
+ const result = handler(input, validityKey);
450
428
  input.error = _checkResult(result, input.value);
451
429
  if (input.error) return;
452
430
  }
453
431
  for (const index in inputValidator.handlers) {
454
- const result = inputValidator.handlers[index](input, fields);
432
+ const handler = inputValidator.handlers[index];
433
+ const result = handler(input, fields);
455
434
  input.error = _checkResult(result, input.value);
456
435
  if (input.error) return;
457
436
  }
@@ -461,7 +440,8 @@ const useFormHandlers = (getState, setState, validators = {}, optimized = false)
461
440
  _debounce(input.debounce || 300, `${input.gid}-async`).then(() => {
462
441
  const validateAsync = async () => {
463
442
  for (const index in inputValidator.asyncHandlers) {
464
- const result = await inputValidator.asyncHandlers[index](input, fields);
443
+ const handler = inputValidator.asyncHandlers[index];
444
+ const result = await handler(input, fields);
465
445
  input.error = _checkResult(result, input.value);
466
446
  if (input.error) break;
467
447
  }
@@ -478,10 +458,6 @@ const useFormHandlers = (getState, setState, validators = {}, optimized = false)
478
458
  });
479
459
  }
480
460
  };
481
- const hasCustomValidation = input => {
482
- const validator = validators[input.validatorKey || input.formKey] || validators['*'];
483
- return validator && (validator.asyncHandlers.length > 0 || validator.handlers.length > 0);
484
- };
485
461
  return {
486
462
  _updateInputHandler,
487
463
  _viHandler,
@@ -496,7 +472,8 @@ const GFormContextProvider = ({
496
472
  children,
497
473
  initialState,
498
474
  validators,
499
- optimized
475
+ optimized,
476
+ formRef
500
477
  }) => {
501
478
  const stateRef = React.useRef(initialState);
502
479
  const listeners = React.useRef(null);
@@ -507,19 +484,59 @@ const GFormContextProvider = ({
507
484
  return;
508
485
  }
509
486
  stateRef.current = nextState;
510
- listeners.current.forEach(l => l());
487
+ listeners.current.forEach(listener => listener());
511
488
  }, []);
512
489
  const handlers = useFormHandlers(() => stateRef.current, setState, validators, optimized);
490
+ const getInputElement = React.useCallback(formKey => {
491
+ if (formRef && formRef.current) {
492
+ return formRef.current[formKey];
493
+ }
494
+ }, []);
495
+ const registerField = React.useCallback(config => {
496
+ const prev = stateRef.current;
497
+ if (prev.fields[config.formKey]) {
498
+ {
499
+ console.warn(`DEV ONLY - [Duplicate Keys] - field with key '${config.formKey}' already defined.`);
500
+ }
501
+ return;
502
+ }
503
+ const inputState = _buildInputInitialValues(config);
504
+ stateRef.current = {
505
+ ...prev,
506
+ fields: {
507
+ ...prev.fields,
508
+ [config.formKey]: {
509
+ ...inputState,
510
+ dispatchChanges: changes => handlers._dispatchChanges(changes, config.formKey)
511
+ }
512
+ }
513
+ };
514
+ }, [handlers]);
515
+ const unregisterField = React.useCallback(formKey => {
516
+ const prev = stateRef.current;
517
+ if (!prev.fields[formKey]) return;
518
+ const {
519
+ [formKey]: _,
520
+ ...remainingFields
521
+ } = prev.fields;
522
+ stateRef.current = {
523
+ ...prev,
524
+ fields: remainingFields
525
+ };
526
+ listeners.current.forEach(listener => listener());
527
+ }, []);
513
528
  const store = React.useMemo(() => {
514
529
  if (!listeners.current) {
515
530
  listeners.current = new Set();
531
+ stateRef.current = initialState;
532
+ for (const fieldKey in initialState.fields) {
533
+ initialState.fields[fieldKey].dispatchChanges = changes => handlers._dispatchChanges(changes, fieldKey);
534
+ }
516
535
  } else {
517
536
  listeners.current.clear();
518
- _copyStateFields(stateRef.current, initialState);
519
- }
520
- stateRef.current = initialState;
521
- for (const fieldKey in initialState.fields) {
522
- initialState.fields[fieldKey].dispatchChanges = changes => handlers._dispatchChanges(changes, fieldKey);
537
+ for (const fieldKey in stateRef.current.fields) {
538
+ stateRef.current.fields[fieldKey].dispatchChanges = changes => handlers._dispatchChanges(changes, fieldKey);
539
+ }
523
540
  }
524
541
  return {
525
542
  getState: () => stateRef.current,
@@ -528,7 +545,10 @@ const GFormContextProvider = ({
528
545
  listeners.current.add(listener);
529
546
  return () => listeners.current.delete(listener);
530
547
  },
531
- handlers
548
+ handlers,
549
+ registerField,
550
+ unregisterField,
551
+ getInputElement
532
552
  };
533
553
  }, [initialState]);
534
554
  return React.createElement(GFormContext.Provider, {
@@ -537,7 +557,7 @@ const GFormContextProvider = ({
537
557
  };
538
558
  const useFormStore = () => {
539
559
  const store = React.useContext(GFormContext);
540
- if (!store.getState) throw new Error('useGFormStore must be used within `GForm` component');
560
+ if (!store.getState) throw new Error('useFormStore must be used within `GForm` component');
541
561
  return store;
542
562
  };
543
563
  const useFormSelector = selector => {
@@ -567,9 +587,9 @@ const FormRenderer = React.forwardRef(({
567
587
  onKeyUp,
568
588
  children,
569
589
  onInit,
590
+ formRef,
570
591
  ...rest
571
592
  }, ref) => {
572
- const formRef = React.useRef(null);
573
593
  const {
574
594
  handlers,
575
595
  getState
@@ -663,15 +683,18 @@ const GForm = React.forwardRef(({
663
683
  optimized,
664
684
  ...props
665
685
  }, ref) => {
666
- const initialState = React.useMemo(() => {
667
- return _buildFormInitialValues(typeof children === 'function' ? children({}) : children);
668
- }, [children]);
686
+ const initialState = React.useMemo(() => ({
687
+ fields: {}
688
+ }), []);
689
+ const formRef = React.useRef(null);
669
690
  return React.createElement(GFormContextProvider, {
670
691
  initialState: initialState,
692
+ formRef: formRef,
671
693
  validators: validators,
672
694
  optimized: optimized
673
695
  }, React.createElement(FormRenderer, _extends({
674
- ref: ref
696
+ ref: ref,
697
+ formRef: formRef
675
698
  }, props), children));
676
699
  });
677
700
 
@@ -681,27 +704,42 @@ const makeSelectFields = (keys = []) => createSelector(selectFields, fields => {
681
704
  return selected.length ? selected : null;
682
705
  });
683
706
 
684
- const _GInput = React.forwardRef(({
685
- formKey,
686
- element,
687
- title,
688
- type = 'text',
689
- fetch,
690
- fetchDeps,
691
- optimized,
692
- debounce = 300,
693
- defaultChecked,
694
- defaultValue,
695
- checked,
696
- validatorKey,
697
- value,
698
- ...rest
699
- }, ref) => {
700
- const inputState = useFormSelector(state => state.fields[formKey]);
707
+ const _GInput = React.forwardRef((props, ref) => {
701
708
  const store = useFormStore();
709
+ const {
710
+ formKey,
711
+ element,
712
+ title,
713
+ type = 'text',
714
+ fetch,
715
+ fetchDeps,
716
+ optimized,
717
+ debounce = 300,
718
+ defaultChecked,
719
+ defaultValue,
720
+ checked,
721
+ validatorKey,
722
+ value,
723
+ ...rest
724
+ } = props;
725
+ if (!store.getState().fields[formKey]) {
726
+ store.registerField(props);
727
+ }
728
+ const inputState = useFormSelector(state => state.fields[formKey]);
729
+ const _fetchDeps = useFormSelector(makeSelectFields(fetchDeps));
730
+ React.useEffect(() => {
731
+ if (inputState.value) {
732
+ store.handlers._viHandler(inputState, {
733
+ target: store.getInputElement(formKey)
734
+ });
735
+ }
736
+ return () => {
737
+ store.unregisterField(formKey);
738
+ };
739
+ }, []);
702
740
  const _element = React.useMemo(() => {
703
741
  let value, checked;
704
- if (type === 'checkbox') checked = inputState.value || false;else value = inputState.value || '';
742
+ if (type === 'checkbox') checked = inputState.value || false;else if (type === 'number') value = inputState.value || 0;else value = inputState.value || '';
705
743
  const _props = {
706
744
  ...rest,
707
745
  type,
@@ -734,25 +772,12 @@ const _GInput = React.forwardRef(({
734
772
  } : (e, unknown) => {
735
773
  store.handlers._updateInputHandler(inputState, e, unknown);
736
774
  };
737
- if (!inputState.touched && inputState.dispatchChanges) {
738
- _props.onFocus = rest.onFocus ? e => {
739
- rest.onFocus(e);
740
- inputState.dispatchChanges({
741
- touched: true
742
- });
743
- } : () => {
744
- inputState.dispatchChanges({
745
- touched: true
746
- });
747
- };
748
- }
749
775
  }
750
776
  if (element) {
751
777
  return element(inputState, _props);
752
778
  }
753
779
  return React.createElement("input", _props);
754
780
  }, [inputState, element]);
755
- const _fetchDeps = useFormSelector(makeSelectFields(fetchDeps));
756
781
  React.useEffect(() => {
757
782
  if (fetch) {
758
783
  _debounce(debounce, `${inputState.gid}-fetch`).then(() => {