@faasjs/react 4.0.0 → 4.1.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.
package/README.md CHANGED
@@ -16,13 +16,17 @@ React plugin for FaasJS.
16
16
  - Additional React functions:
17
17
  - Utils:
18
18
  - [equal](functions/equal.md): Compare two values for deep equality.
19
+ - [createSplittingContext](functions/createSplittingContext.md): Create a context for code splitting.
20
+ - [splittingState](functions/splittingState.md): Create a splitting states.
21
+ - Hooks:
19
22
  - [useEqualMemoize](functions/useEqualMemoize.md): Memoize a value with deep equality.
20
23
  - [useEqualEffect](functions/useEqualEffect.md): Run an effect with deep equality.
21
24
  - [useEqualMemo](functions/useEqualMemo.md): Memoize a value with deep equality.
22
25
  - [useEqualCallback](functions/useEqualCallback.md): Memoize a callback with deep equality.
23
26
  - [useConstant](functions/useConstant.md): Create a constant value with hooks.
24
- - [createSplittingContext](functions/createSplittingContext.md): Create a context for code splitting.
25
- - [splittingState](functions/splittingState.md): Create a splitting states.
27
+ - [usePrevious](functions/usePrevious.md): Get the previous value of a state.
28
+ - [useStateRef](functions/useStateRef.md): Create a state with a ref.
29
+ - Components:
26
30
  - [OptionalWrapper](functions/OptionalWrapper.md): Render a component optionally.
27
31
  - [ErrorBoundary](classes/ErrorBoundary.md): Catch errors in the component tree.
28
32
  - Fetch Data:
@@ -58,6 +62,7 @@ npm install @faasjs/react react
58
62
  - [useFormContext](functions/useFormContext.md)
59
63
  - [usePrevious](functions/usePrevious.md)
60
64
  - [useSplittingState](functions/useSplittingState.md)
65
+ - [useStateRef](functions/useStateRef.md)
61
66
  - [validValues](functions/validValues.md)
62
67
  - [withFaasData](functions/withFaasData.md)
63
68
 
package/dist/index.d.mts CHANGED
@@ -3,7 +3,7 @@ export { FaasAction, FaasData, FaasParams } from '@faasjs/types';
3
3
  import { Response, BaseUrl, ResponseError, Options, FaasBrowserClient } from '@faasjs/browser';
4
4
  export { Options, Response, ResponseError, ResponseHeaders } from '@faasjs/browser';
5
5
  import * as react from 'react';
6
- import { ReactNode, Dispatch, SetStateAction, JSX, ReactElement, Component, ComponentType, ComponentProps, JSXElementConstructor } from 'react';
6
+ import { ReactNode, Dispatch, SetStateAction, RefObject, JSX, ReactElement, Component, ComponentType, ComponentProps, JSXElementConstructor } from 'react';
7
7
  import * as react_jsx_runtime from 'react/jsx-runtime';
8
8
 
9
9
  /**
@@ -57,14 +57,6 @@ declare function useEqualMemo<T>(callback: () => T, dependencies: any[]): T;
57
57
  * @returns The result of the `useCallback` hook with memoized dependencies.
58
58
  */
59
59
  declare function useEqualCallback<T extends (...args: any[]) => any>(callback: T, dependencies: any[]): T;
60
- /**
61
- * Hook to store the previous value of a state or prop.
62
- *
63
- * @template T - The type of the value.
64
- * @param {T} value - The current value to be stored.
65
- * @returns {T | undefined} - The previous value, or undefined if there is no previous value.
66
- */
67
- declare function usePrevious<T>(value: T): T | undefined;
68
60
 
69
61
  /**
70
62
  * Creates a splitting context with the given default value.
@@ -197,6 +189,39 @@ type StatesWithSetters<T> = T & StateSetters<T>;
197
189
  */
198
190
  declare function useSplittingState<T extends Record<string, unknown>>(initialStates: T): StatesWithSetters<T>;
199
191
 
192
+ /**
193
+ * Hook to store the previous value of a state or prop.
194
+ *
195
+ * @template T - The type of the value.
196
+ * @param {T} value - The current value to be stored.
197
+ * @returns {T | undefined} - The previous value, or undefined if there is no previous value.
198
+ */
199
+ declare function usePrevious<T = any>(value: T): T | undefined;
200
+
201
+ /**
202
+ * Custom hook that returns a stateful value and a ref to that value.
203
+ *
204
+ * @template T - The type of the value.
205
+ * @param {T} initialValue - The initial value of the state.
206
+ * @returns {[T, (value: T) => void, RefObject<T>]} - The stateful value, a function to set the value, and a ref to the value.
207
+ *
208
+ * @example
209
+ * ```tsx
210
+ * import { useStateRef } from '@faasjs/react'
211
+ *
212
+ * function MyComponent() {
213
+ * const [value, setValue, ref] = useStateRef(0)
214
+ *
215
+ * return (
216
+ * <div>
217
+ * <p>Value: {value}</p>
218
+ * <button onClick={() => setValue(value + 1)}>Increment</button>
219
+ * <button onClick={() => console.log(ref.current)}>Submit</button>
220
+ * </div>
221
+ * )
222
+ */
223
+ declare function useStateRef<T = any>(initialValue?: T): [T, Dispatch<SetStateAction<T>>, RefObject<T>];
224
+
200
225
  /**
201
226
  * Injects FaasData props.
202
227
  */
@@ -413,7 +438,7 @@ declare const OptionalWrapper: React.FC<OptionalWrapperProps> & {
413
438
  */
414
439
  type FormButtonElementProps = {
415
440
  children?: React.ReactNode;
416
- submitting?: boolean;
441
+ submitting: boolean;
417
442
  submit: () => Promise<void>;
418
443
  };
419
444
 
@@ -573,14 +598,14 @@ type FormProps<Values extends Record<string, any> = Record<string, any>, FormEle
573
598
  * }
574
599
  * ```
575
600
  */
576
- declare function FormContainer<Values extends Record<string, any> = Record<string, any>, FormElements extends FormElementTypes = FormElementTypes, Rules extends FormRules = typeof FormDefaultRules>({ defaultValues, Elements, rules, lang, ...props }: FormProps<Values, FormElements, Rules>): react_jsx_runtime.JSX.Element;
601
+ declare function FormContainer<Values extends Record<string, any> = Record<string, any>, FormElements extends FormElementTypes = FormElementTypes, Rules extends FormRules = typeof FormDefaultRules>({ defaultValues, Elements, rules, lang, items, ...props }: FormProps<Values, FormElements, Rules>): react_jsx_runtime.JSX.Element;
577
602
  declare namespace FormContainer {
578
603
  var displayName: string;
579
604
  var whyDidYouRender: boolean;
580
605
  }
581
606
 
582
- type FormContextProps<Values extends Record<string, any> = Record<string, any>, Rules extends FormRules = typeof FormDefaultRules> = {
583
- items: FormLabelElementProps[];
607
+ type FormContextProps<Values extends Record<string, any> = Record<string, any>, FormElements extends FormElementTypes = FormElementTypes, Rules extends FormRules = typeof FormDefaultRules> = {
608
+ items: FormItemProps<FormElements, InferFormRulesOptions<Rules>>[];
584
609
  onSubmit: (values: Values) => Promise<void>;
585
610
  Elements: FormElementTypes;
586
611
  lang: FormLang;
@@ -591,13 +616,14 @@ type FormContextProps<Values extends Record<string, any> = Record<string, any>,
591
616
  setValues: Dispatch<SetStateAction<Values>>;
592
617
  errors: Record<string, Error>;
593
618
  setErrors: Dispatch<SetStateAction<Record<string, Error>>>;
619
+ valuesRef: RefObject<Values>;
594
620
  };
595
- declare const FormContextProvider: <NewT extends FormContextProps<Record<string, any>, FormRules> = FormContextProps<Record<string, any>, FormRules>>(props: {
621
+ declare const FormContextProvider: <NewT extends FormContextProps<Record<string, any>, FormElementTypes, FormRules> = FormContextProps<Record<string, any>, FormElementTypes, FormRules>>(props: {
596
622
  value?: Partial<NewT>;
597
623
  children: react.ReactNode;
598
624
  memo?: true | any[];
599
625
  initializeStates?: Partial<NewT>;
600
626
  }) => react.ReactNode;
601
- declare const useFormContext: <NewT extends FormContextProps<Record<string, any>, FormRules> = FormContextProps<Record<string, any>, FormRules>>() => Readonly<NewT>;
627
+ declare const useFormContext: <NewT extends FormContextProps<Record<string, any>, FormElementTypes, FormRules> = FormContextProps<Record<string, any>, FormElementTypes, FormRules>>() => Readonly<NewT>;
602
628
 
603
- export { ErrorBoundary, type ErrorBoundaryProps, type ErrorChildrenProps, type FaasDataInjection, FaasDataWrapper, type FaasDataWrapperProps, FaasReactClient, type FaasReactClientInstance, type FaasReactClientOptions, FormContainer as Form, type FormButtonElementProps, type FormContextProps, FormContextProvider, FormDefaultElements, FormDefaultLang, FormDefaultRules, type FormDefaultRulesOptions, type FormElementTypes, type FormInputElementProps, FormItem, type FormItemName, type FormItemProps, type FormLabelElementProps, type FormLang, type FormProps, type FormRule, type FormRules, type InferFormRulesOptions, type OnError, OptionalWrapper, type OptionalWrapperProps, createSplittingContext, equal, faas, getClient, useConstant, useEqualCallback, useEqualEffect, useEqualMemo, useEqualMemoize, useFaas, type useFaasOptions, useFormContext, usePrevious, useSplittingState, validValues, withFaasData };
629
+ export { ErrorBoundary, type ErrorBoundaryProps, type ErrorChildrenProps, type FaasDataInjection, FaasDataWrapper, type FaasDataWrapperProps, FaasReactClient, type FaasReactClientInstance, type FaasReactClientOptions, FormContainer as Form, type FormButtonElementProps, type FormContextProps, FormContextProvider, FormDefaultElements, FormDefaultLang, FormDefaultRules, type FormDefaultRulesOptions, type FormElementTypes, type FormInputElementProps, FormItem, type FormItemName, type FormItemProps, type FormLabelElementProps, type FormLang, type FormProps, type FormRule, type FormRules, type InferFormRulesOptions, type OnError, OptionalWrapper, type OptionalWrapperProps, createSplittingContext, equal, faas, getClient, useConstant, useEqualCallback, useEqualEffect, useEqualMemo, useEqualMemoize, useFaas, type useFaasOptions, useFormContext, usePrevious, useSplittingState, useStateRef, validValues, withFaasData };
package/dist/index.d.ts CHANGED
@@ -3,7 +3,7 @@ export { FaasAction, FaasData, FaasParams } from '@faasjs/types';
3
3
  import { Response, BaseUrl, ResponseError, Options, FaasBrowserClient } from '@faasjs/browser';
4
4
  export { Options, Response, ResponseError, ResponseHeaders } from '@faasjs/browser';
5
5
  import * as react from 'react';
6
- import { ReactNode, Dispatch, SetStateAction, JSX, ReactElement, Component, ComponentType, ComponentProps, JSXElementConstructor } from 'react';
6
+ import { ReactNode, Dispatch, SetStateAction, RefObject, JSX, ReactElement, Component, ComponentType, ComponentProps, JSXElementConstructor } from 'react';
7
7
  import * as react_jsx_runtime from 'react/jsx-runtime';
8
8
 
9
9
  /**
@@ -57,14 +57,6 @@ declare function useEqualMemo<T>(callback: () => T, dependencies: any[]): T;
57
57
  * @returns The result of the `useCallback` hook with memoized dependencies.
58
58
  */
59
59
  declare function useEqualCallback<T extends (...args: any[]) => any>(callback: T, dependencies: any[]): T;
60
- /**
61
- * Hook to store the previous value of a state or prop.
62
- *
63
- * @template T - The type of the value.
64
- * @param {T} value - The current value to be stored.
65
- * @returns {T | undefined} - The previous value, or undefined if there is no previous value.
66
- */
67
- declare function usePrevious<T>(value: T): T | undefined;
68
60
 
69
61
  /**
70
62
  * Creates a splitting context with the given default value.
@@ -197,6 +189,39 @@ type StatesWithSetters<T> = T & StateSetters<T>;
197
189
  */
198
190
  declare function useSplittingState<T extends Record<string, unknown>>(initialStates: T): StatesWithSetters<T>;
199
191
 
192
+ /**
193
+ * Hook to store the previous value of a state or prop.
194
+ *
195
+ * @template T - The type of the value.
196
+ * @param {T} value - The current value to be stored.
197
+ * @returns {T | undefined} - The previous value, or undefined if there is no previous value.
198
+ */
199
+ declare function usePrevious<T = any>(value: T): T | undefined;
200
+
201
+ /**
202
+ * Custom hook that returns a stateful value and a ref to that value.
203
+ *
204
+ * @template T - The type of the value.
205
+ * @param {T} initialValue - The initial value of the state.
206
+ * @returns {[T, (value: T) => void, RefObject<T>]} - The stateful value, a function to set the value, and a ref to the value.
207
+ *
208
+ * @example
209
+ * ```tsx
210
+ * import { useStateRef } from '@faasjs/react'
211
+ *
212
+ * function MyComponent() {
213
+ * const [value, setValue, ref] = useStateRef(0)
214
+ *
215
+ * return (
216
+ * <div>
217
+ * <p>Value: {value}</p>
218
+ * <button onClick={() => setValue(value + 1)}>Increment</button>
219
+ * <button onClick={() => console.log(ref.current)}>Submit</button>
220
+ * </div>
221
+ * )
222
+ */
223
+ declare function useStateRef<T = any>(initialValue?: T): [T, Dispatch<SetStateAction<T>>, RefObject<T>];
224
+
200
225
  /**
201
226
  * Injects FaasData props.
202
227
  */
@@ -413,7 +438,7 @@ declare const OptionalWrapper: React.FC<OptionalWrapperProps> & {
413
438
  */
414
439
  type FormButtonElementProps = {
415
440
  children?: React.ReactNode;
416
- submitting?: boolean;
441
+ submitting: boolean;
417
442
  submit: () => Promise<void>;
418
443
  };
419
444
 
@@ -573,14 +598,14 @@ type FormProps<Values extends Record<string, any> = Record<string, any>, FormEle
573
598
  * }
574
599
  * ```
575
600
  */
576
- declare function FormContainer<Values extends Record<string, any> = Record<string, any>, FormElements extends FormElementTypes = FormElementTypes, Rules extends FormRules = typeof FormDefaultRules>({ defaultValues, Elements, rules, lang, ...props }: FormProps<Values, FormElements, Rules>): react_jsx_runtime.JSX.Element;
601
+ declare function FormContainer<Values extends Record<string, any> = Record<string, any>, FormElements extends FormElementTypes = FormElementTypes, Rules extends FormRules = typeof FormDefaultRules>({ defaultValues, Elements, rules, lang, items, ...props }: FormProps<Values, FormElements, Rules>): react_jsx_runtime.JSX.Element;
577
602
  declare namespace FormContainer {
578
603
  var displayName: string;
579
604
  var whyDidYouRender: boolean;
580
605
  }
581
606
 
582
- type FormContextProps<Values extends Record<string, any> = Record<string, any>, Rules extends FormRules = typeof FormDefaultRules> = {
583
- items: FormLabelElementProps[];
607
+ type FormContextProps<Values extends Record<string, any> = Record<string, any>, FormElements extends FormElementTypes = FormElementTypes, Rules extends FormRules = typeof FormDefaultRules> = {
608
+ items: FormItemProps<FormElements, InferFormRulesOptions<Rules>>[];
584
609
  onSubmit: (values: Values) => Promise<void>;
585
610
  Elements: FormElementTypes;
586
611
  lang: FormLang;
@@ -591,13 +616,14 @@ type FormContextProps<Values extends Record<string, any> = Record<string, any>,
591
616
  setValues: Dispatch<SetStateAction<Values>>;
592
617
  errors: Record<string, Error>;
593
618
  setErrors: Dispatch<SetStateAction<Record<string, Error>>>;
619
+ valuesRef: RefObject<Values>;
594
620
  };
595
- declare const FormContextProvider: <NewT extends FormContextProps<Record<string, any>, FormRules> = FormContextProps<Record<string, any>, FormRules>>(props: {
621
+ declare const FormContextProvider: <NewT extends FormContextProps<Record<string, any>, FormElementTypes, FormRules> = FormContextProps<Record<string, any>, FormElementTypes, FormRules>>(props: {
596
622
  value?: Partial<NewT>;
597
623
  children: react.ReactNode;
598
624
  memo?: true | any[];
599
625
  initializeStates?: Partial<NewT>;
600
626
  }) => react.ReactNode;
601
- declare const useFormContext: <NewT extends FormContextProps<Record<string, any>, FormRules> = FormContextProps<Record<string, any>, FormRules>>() => Readonly<NewT>;
627
+ declare const useFormContext: <NewT extends FormContextProps<Record<string, any>, FormElementTypes, FormRules> = FormContextProps<Record<string, any>, FormElementTypes, FormRules>>() => Readonly<NewT>;
602
628
 
603
- export { ErrorBoundary, type ErrorBoundaryProps, type ErrorChildrenProps, type FaasDataInjection, FaasDataWrapper, type FaasDataWrapperProps, FaasReactClient, type FaasReactClientInstance, type FaasReactClientOptions, FormContainer as Form, type FormButtonElementProps, type FormContextProps, FormContextProvider, FormDefaultElements, FormDefaultLang, FormDefaultRules, type FormDefaultRulesOptions, type FormElementTypes, type FormInputElementProps, FormItem, type FormItemName, type FormItemProps, type FormLabelElementProps, type FormLang, type FormProps, type FormRule, type FormRules, type InferFormRulesOptions, type OnError, OptionalWrapper, type OptionalWrapperProps, createSplittingContext, equal, faas, getClient, useConstant, useEqualCallback, useEqualEffect, useEqualMemo, useEqualMemoize, useFaas, type useFaasOptions, useFormContext, usePrevious, useSplittingState, validValues, withFaasData };
629
+ export { ErrorBoundary, type ErrorBoundaryProps, type ErrorChildrenProps, type FaasDataInjection, FaasDataWrapper, type FaasDataWrapperProps, FaasReactClient, type FaasReactClientInstance, type FaasReactClientOptions, FormContainer as Form, type FormButtonElementProps, type FormContextProps, FormContextProvider, FormDefaultElements, FormDefaultLang, FormDefaultRules, type FormDefaultRulesOptions, type FormElementTypes, type FormInputElementProps, FormItem, type FormItemName, type FormItemProps, type FormLabelElementProps, type FormLang, type FormProps, type FormRule, type FormRules, type InferFormRulesOptions, type OnError, OptionalWrapper, type OptionalWrapperProps, createSplittingContext, equal, faas, getClient, useConstant, useEqualCallback, useEqualEffect, useEqualMemo, useEqualMemoize, useFaas, type useFaasOptions, useFormContext, usePrevious, useSplittingState, useStateRef, validValues, withFaasData };
package/dist/index.js CHANGED
@@ -77,13 +77,6 @@ function useEqualCallback(callback, dependencies) {
77
77
  useEqualMemoize(dependencies)
78
78
  );
79
79
  }
80
- function usePrevious(value) {
81
- const ref = react.useRef(void 0);
82
- react.useEffect(() => {
83
- ref.current = value;
84
- });
85
- return ref.current;
86
- }
87
80
  function useSplittingState(initialStates) {
88
81
  const states = {};
89
82
  for (const key of Object.keys(initialStates)) {
@@ -135,6 +128,21 @@ function createSplittingContext(defaultValue) {
135
128
  use
136
129
  };
137
130
  }
131
+ function usePrevious(value) {
132
+ const ref = react.useRef(void 0);
133
+ react.useEffect(() => {
134
+ ref.current = value;
135
+ });
136
+ return ref.current;
137
+ }
138
+ function useStateRef(initialValue) {
139
+ const [state, setState] = react.useState(initialValue ?? null);
140
+ const ref = react.useRef(state);
141
+ react.useEffect(() => {
142
+ ref.current = state;
143
+ }, [state]);
144
+ return [state, setState, ref];
145
+ }
138
146
  function FaasDataWrapper(props) {
139
147
  const request = getClient(props.baseUrl).useFaas(
140
148
  props.action,
@@ -354,13 +362,14 @@ var FormContext = createSplittingContext([
354
362
  "onSubmit",
355
363
  "Elements",
356
364
  "lang",
365
+ "rules",
357
366
  "submitting",
358
367
  "setSubmitting",
359
368
  "values",
360
369
  "setValues",
361
370
  "errors",
362
371
  "setErrors",
363
- "rules"
372
+ "valuesRef"
364
373
  ]);
365
374
  var FormContextProvider = FormContext.Provider;
366
375
  var useFormContext = FormContext.use;
@@ -468,38 +477,37 @@ function FormFooter() {
468
477
  submitting,
469
478
  setSubmitting,
470
479
  onSubmit,
471
- values,
480
+ valuesRef,
472
481
  Elements,
473
482
  items,
474
483
  setErrors,
475
484
  lang,
476
485
  rules
477
486
  } = useFormContext();
478
- return /* @__PURE__ */ jsxRuntime.jsx(
479
- Elements.Button,
480
- {
481
- submitting,
482
- submit: async () => {
483
- setSubmitting(true);
484
- const errors = await validValues(rules, items, values, lang);
485
- if (Object.keys(errors).length) {
486
- setErrors(errors);
487
- setSubmitting(false);
488
- return;
489
- }
490
- onSubmit(values).finally(() => setSubmitting(false));
491
- },
492
- children: lang.submit
487
+ const handleSubmit = react.useCallback(async () => {
488
+ setSubmitting(true);
489
+ setErrors({});
490
+ const errors = await validValues(rules, items, valuesRef.current, lang);
491
+ if (Object.keys(errors).length) {
492
+ setErrors(errors);
493
+ setSubmitting(false);
494
+ return;
493
495
  }
496
+ onSubmit(valuesRef.current).finally(() => setSubmitting(false));
497
+ }, [setSubmitting, setErrors, rules, items, lang, onSubmit]);
498
+ const MemoizedButton = react.useMemo(
499
+ () => /* @__PURE__ */ jsxRuntime.jsx(Elements.Button, { submitting, submit: handleSubmit, children: lang.submit }),
500
+ [submitting, handleSubmit, lang.submit]
494
501
  );
502
+ return MemoizedButton;
495
503
  }
496
504
  FormFooter.displayName = "FormFooter";
497
505
  FormFooter.whyDidYouRender = true;
498
- var FormButtonElement = react.forwardRef(({ children, submit, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
506
+ var FormButtonElement = react.forwardRef(({ children, submit, submitting, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
499
507
  "button",
500
508
  {
501
509
  type: "button",
502
- disabled: props.submitting,
510
+ disabled: submitting,
503
511
  onClick: submit,
504
512
  ...props,
505
513
  ref,
@@ -553,23 +561,29 @@ function FormContainer({
553
561
  Elements,
554
562
  rules,
555
563
  lang,
564
+ items,
556
565
  ...props
557
566
  }) {
567
+ const [values, setValues, valuesRef] = useStateRef(
568
+ mergeValues(items, defaultValues)
569
+ );
558
570
  return /* @__PURE__ */ jsxRuntime.jsxs(
559
571
  FormContextProvider,
560
572
  {
561
573
  initializeStates: {
562
- values: mergeValues(props.items, defaultValues),
563
574
  errors: {},
564
- submitting: false,
565
- Elements: Object.assign(
566
- FormDefaultElements,
567
- Elements
568
- ),
575
+ submitting: false
576
+ },
577
+ value: {
578
+ Elements: Object.assign(FormDefaultElements, Elements),
569
579
  lang: Object.assign(FormDefaultLang, lang),
570
- rules: Object.assign(FormDefaultRules, rules)
580
+ rules: Object.assign(FormDefaultRules, rules),
581
+ items,
582
+ values,
583
+ setValues,
584
+ valuesRef,
585
+ ...props
571
586
  },
572
- value: props,
573
587
  memo: true,
574
588
  children: [
575
589
  /* @__PURE__ */ jsxRuntime.jsx(FormBody, {}),
@@ -604,5 +618,6 @@ exports.useFaas = useFaas;
604
618
  exports.useFormContext = useFormContext;
605
619
  exports.usePrevious = usePrevious;
606
620
  exports.useSplittingState = useSplittingState;
621
+ exports.useStateRef = useStateRef;
607
622
  exports.validValues = validValues;
608
623
  exports.withFaasData = withFaasData;
package/dist/index.mjs CHANGED
@@ -75,13 +75,6 @@ function useEqualCallback(callback, dependencies) {
75
75
  useEqualMemoize(dependencies)
76
76
  );
77
77
  }
78
- function usePrevious(value) {
79
- const ref = useRef(void 0);
80
- useEffect(() => {
81
- ref.current = value;
82
- });
83
- return ref.current;
84
- }
85
78
  function useSplittingState(initialStates) {
86
79
  const states = {};
87
80
  for (const key of Object.keys(initialStates)) {
@@ -133,6 +126,21 @@ function createSplittingContext(defaultValue) {
133
126
  use
134
127
  };
135
128
  }
129
+ function usePrevious(value) {
130
+ const ref = useRef(void 0);
131
+ useEffect(() => {
132
+ ref.current = value;
133
+ });
134
+ return ref.current;
135
+ }
136
+ function useStateRef(initialValue) {
137
+ const [state, setState] = useState(initialValue ?? null);
138
+ const ref = useRef(state);
139
+ useEffect(() => {
140
+ ref.current = state;
141
+ }, [state]);
142
+ return [state, setState, ref];
143
+ }
136
144
  function FaasDataWrapper(props) {
137
145
  const request = getClient(props.baseUrl).useFaas(
138
146
  props.action,
@@ -352,13 +360,14 @@ var FormContext = createSplittingContext([
352
360
  "onSubmit",
353
361
  "Elements",
354
362
  "lang",
363
+ "rules",
355
364
  "submitting",
356
365
  "setSubmitting",
357
366
  "values",
358
367
  "setValues",
359
368
  "errors",
360
369
  "setErrors",
361
- "rules"
370
+ "valuesRef"
362
371
  ]);
363
372
  var FormContextProvider = FormContext.Provider;
364
373
  var useFormContext = FormContext.use;
@@ -466,38 +475,37 @@ function FormFooter() {
466
475
  submitting,
467
476
  setSubmitting,
468
477
  onSubmit,
469
- values,
478
+ valuesRef,
470
479
  Elements,
471
480
  items,
472
481
  setErrors,
473
482
  lang,
474
483
  rules
475
484
  } = useFormContext();
476
- return /* @__PURE__ */ jsx(
477
- Elements.Button,
478
- {
479
- submitting,
480
- submit: async () => {
481
- setSubmitting(true);
482
- const errors = await validValues(rules, items, values, lang);
483
- if (Object.keys(errors).length) {
484
- setErrors(errors);
485
- setSubmitting(false);
486
- return;
487
- }
488
- onSubmit(values).finally(() => setSubmitting(false));
489
- },
490
- children: lang.submit
485
+ const handleSubmit = useCallback(async () => {
486
+ setSubmitting(true);
487
+ setErrors({});
488
+ const errors = await validValues(rules, items, valuesRef.current, lang);
489
+ if (Object.keys(errors).length) {
490
+ setErrors(errors);
491
+ setSubmitting(false);
492
+ return;
491
493
  }
494
+ onSubmit(valuesRef.current).finally(() => setSubmitting(false));
495
+ }, [setSubmitting, setErrors, rules, items, lang, onSubmit]);
496
+ const MemoizedButton = useMemo(
497
+ () => /* @__PURE__ */ jsx(Elements.Button, { submitting, submit: handleSubmit, children: lang.submit }),
498
+ [submitting, handleSubmit, lang.submit]
492
499
  );
500
+ return MemoizedButton;
493
501
  }
494
502
  FormFooter.displayName = "FormFooter";
495
503
  FormFooter.whyDidYouRender = true;
496
- var FormButtonElement = forwardRef(({ children, submit, ...props }, ref) => /* @__PURE__ */ jsx(
504
+ var FormButtonElement = forwardRef(({ children, submit, submitting, ...props }, ref) => /* @__PURE__ */ jsx(
497
505
  "button",
498
506
  {
499
507
  type: "button",
500
- disabled: props.submitting,
508
+ disabled: submitting,
501
509
  onClick: submit,
502
510
  ...props,
503
511
  ref,
@@ -551,23 +559,29 @@ function FormContainer({
551
559
  Elements,
552
560
  rules,
553
561
  lang,
562
+ items,
554
563
  ...props
555
564
  }) {
565
+ const [values, setValues, valuesRef] = useStateRef(
566
+ mergeValues(items, defaultValues)
567
+ );
556
568
  return /* @__PURE__ */ jsxs(
557
569
  FormContextProvider,
558
570
  {
559
571
  initializeStates: {
560
- values: mergeValues(props.items, defaultValues),
561
572
  errors: {},
562
- submitting: false,
563
- Elements: Object.assign(
564
- FormDefaultElements,
565
- Elements
566
- ),
573
+ submitting: false
574
+ },
575
+ value: {
576
+ Elements: Object.assign(FormDefaultElements, Elements),
567
577
  lang: Object.assign(FormDefaultLang, lang),
568
- rules: Object.assign(FormDefaultRules, rules)
578
+ rules: Object.assign(FormDefaultRules, rules),
579
+ items,
580
+ values,
581
+ setValues,
582
+ valuesRef,
583
+ ...props
569
584
  },
570
- value: props,
571
585
  memo: true,
572
586
  children: [
573
587
  /* @__PURE__ */ jsx(FormBody, {}),
@@ -579,4 +593,4 @@ function FormContainer({
579
593
  FormContainer.displayName = "FormContainer";
580
594
  FormContainer.whyDidYouRender = true;
581
595
 
582
- export { ErrorBoundary, FaasDataWrapper, FaasReactClient, FormContainer as Form, FormContextProvider, FormDefaultElements, FormDefaultLang, FormDefaultRules, FormItem, OptionalWrapper, createSplittingContext, equal, faas, getClient, useConstant, useEqualCallback, useEqualEffect, useEqualMemo, useEqualMemoize, useFaas, useFormContext, usePrevious, useSplittingState, validValues, withFaasData };
596
+ export { ErrorBoundary, FaasDataWrapper, FaasReactClient, FormContainer as Form, FormContextProvider, FormDefaultElements, FormDefaultLang, FormDefaultRules, FormItem, OptionalWrapper, createSplittingContext, equal, faas, getClient, useConstant, useEqualCallback, useEqualEffect, useEqualMemo, useEqualMemoize, useFaas, useFormContext, usePrevious, useSplittingState, useStateRef, validValues, withFaasData };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@faasjs/react",
3
- "version": "4.0.0",
3
+ "version": "4.1.0",
4
4
  "license": "MIT",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -34,10 +34,10 @@
34
34
  "dist"
35
35
  ],
36
36
  "peerDependencies": {
37
- "@faasjs/browser": "4.0.0"
37
+ "@faasjs/browser": "4.1.0"
38
38
  },
39
39
  "devDependencies": {
40
- "@faasjs/browser": "4.0.0",
40
+ "@faasjs/browser": "4.1.0",
41
41
  "@types/react": "*",
42
42
  "react": "*"
43
43
  },