diff --git a/src/components/Form/FormProvider.tsx b/src/components/Form/FormProvider.tsx index db52c45751b7..80f52c8053da 100644 --- a/src/components/Form/FormProvider.tsx +++ b/src/components/Form/FormProvider.tsx @@ -3,17 +3,15 @@ import lodashIsEqual from 'lodash/isEqual'; import type {ForwardedRef, MutableRefObject, ReactNode, RefAttributes} from 'react'; import React, {createRef, forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState} from 'react'; import type {NativeSyntheticEvent, StyleProp, TextInputSubmitEditingEventData, ViewStyle} from 'react-native'; -import type {OnyxEntry} from 'react-native-onyx'; -import {withOnyx} from 'react-native-onyx'; +import {useOnyx} from 'react-native-onyx'; import useLocalize from '@hooks/useLocalize'; import * as ValidationUtils from '@libs/ValidationUtils'; import Visibility from '@libs/Visibility'; import * as FormActions from '@userActions/FormActions'; import CONST from '@src/CONST'; -import type {OnyxFormKey} from '@src/ONYXKEYS'; +import type {OnyxFormDraftKey, OnyxFormKey} from '@src/ONYXKEYS'; import ONYXKEYS from '@src/ONYXKEYS'; import type {Form} from '@src/types/form'; -import type {Network} from '@src/types/onyx'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import type {RegisterInput} from './FormContext'; import FormContext from './FormContext'; @@ -41,46 +39,34 @@ function getInitialValueByType(valueType?: ValueTypeKey): InitialDefaultValue { } } -type FormProviderOnyxProps = { - /** Contains the form state that must be accessed outside the component */ - formState: OnyxEntry
; +type FormProviderProps = FormProps & { + /** Children to render. */ + children: ((props: {inputValues: FormOnyxValues}) => ReactNode) | ReactNode; - /** Contains draft values for each input in the form */ - draftValues: OnyxEntry; + /** Callback to validate the form */ + validate?: (values: FormOnyxValues) => FormInputErrors; - /** Information about the network */ - network: OnyxEntry; -}; - -type FormProviderProps = FormProviderOnyxProps & - FormProps & { - /** Children to render. */ - children: ((props: {inputValues: FormOnyxValues}) => ReactNode) | ReactNode; - - /** Callback to validate the form */ - validate?: (values: FormOnyxValues) => FormInputErrors; + /** Should validate function be called when input loose focus */ + shouldValidateOnBlur?: boolean; - /** Should validate function be called when input loose focus */ - shouldValidateOnBlur?: boolean; + /** Should validate function be called when the value of the input is changed */ + shouldValidateOnChange?: boolean; - /** Should validate function be called when the value of the input is changed */ - shouldValidateOnChange?: boolean; + /** Whether to remove invisible characters from strings before validation and submission */ + shouldTrimValues?: boolean; - /** Whether to remove invisible characters from strings before validation and submission */ - shouldTrimValues?: boolean; + /** Styles that will be applied to the submit button only */ + submitButtonStyles?: StyleProp; - /** Styles that will be applied to the submit button only */ - submitButtonStyles?: StyleProp; + /** Whether to apply flex to the submit button */ + submitFlexEnabled?: boolean; - /** Whether to apply flex to the submit button */ - submitFlexEnabled?: boolean; + /** Whether button is disabled */ + isSubmitDisabled?: boolean; - /** Whether button is disabled */ - isSubmitDisabled?: boolean; - - /** Whether HTML is allowed in form inputs */ - allowHTML?: boolean; - }; + /** Whether HTML is allowed in form inputs */ + allowHTML?: boolean; +}; function FormProvider( { @@ -89,10 +75,7 @@ function FormProvider( shouldValidateOnBlur = true, shouldValidateOnChange = true, children, - formState, - network, enabledWhenOffline = false, - draftValues, onSubmit, shouldTrimValues = true, allowHTML = false, @@ -100,6 +83,9 @@ function FormProvider( }: FormProviderProps, forwardedRef: ForwardedRef, ) { + const [network] = useOnyx(ONYXKEYS.NETWORK); + const [formState] = useOnyx(`${formID}`); + const [draftValues] = useOnyx(`${formID}Draft`); const {preferredLocale, translate} = useLocalize(); const inputRefs = useRef({}); const touchedInputs = useRef>({}); @@ -404,19 +390,6 @@ function FormProvider( FormProvider.displayName = 'Form'; -export default withOnyx({ - network: { - key: ONYXKEYS.NETWORK, - }, - // withOnyx typings are not able to handle such generic cases like this one, since it's a generic component we need to cast the keys to any - formState: { - // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-explicit-any - key: ({formID}) => formID as any, - }, - draftValues: { - // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-explicit-any - key: (props) => `${props.formID}Draft` as any, - }, -})(forwardRef(FormProvider)) as (props: Omit & RefAttributes, keyof FormProviderOnyxProps>) => ReactNode; +export default forwardRef(FormProvider) as (props: FormProviderProps & RefAttributes) => ReactNode; export type {FormProviderProps}; diff --git a/src/stories/Form.stories.tsx b/src/stories/Form.stories.tsx index ab29612b0556..c15fcb982239 100644 --- a/src/stories/Form.stories.tsx +++ b/src/stories/Form.stories.tsx @@ -2,6 +2,7 @@ import type {Meta, StoryFn} from '@storybook/react'; import React, {useState} from 'react'; import type {ComponentType} from 'react'; import {View} from 'react-native'; +import type {OnyxEntry} from 'react-native-onyx'; import AddressSearch from '@components/AddressSearch'; import CheckboxWithLabel from '@components/CheckboxWithLabel'; import DatePicker from '@components/DatePicker'; @@ -18,8 +19,10 @@ import * as FormActions from '@userActions/FormActions'; import CONST from '@src/CONST'; import type {OnyxFormValuesMapping} from '@src/ONYXKEYS'; import {defaultStyles} from '@src/styles'; +import type {Form} from '@src/types/form'; +import type {Network} from '@src/types/onyx'; -type FormStory = StoryFn; +type FormStory = StoryFn; type StorybookFormValues = { routingNumber?: string; @@ -32,6 +35,17 @@ type StorybookFormValues = { checkbox?: boolean; }; +type FormProviderOnyxProps = { + /** Contains the form state that must be accessed outside the component */ + formState: OnyxEntry; + + /** Contains draft values for each input in the form */ + draftValues: OnyxEntry; + + /** Information about the network */ + network: OnyxEntry; +}; + type StorybookFormErrors = Partial>; const STORYBOOK_FORM_ID = 'TestForm' as keyof OnyxFormValuesMapping; @@ -50,7 +64,7 @@ const story: Meta = { }, }; -function Template(props: FormProviderProps) { +function Template(props: FormProviderProps & FormProviderOnyxProps) { // Form consumes data from Onyx, so we initialize Onyx with the necessary data here NetworkConnection.setOfflineStatus(false); FormActions.setIsLoading(props.formID, !!props.formState?.isLoading); @@ -162,7 +176,7 @@ function Template(props: FormProviderProps) { /** * Story to exhibit the native event handlers for TextInput in the Form Component */ -function WithNativeEventHandler(props: FormProviderProps) { +function WithNativeEventHandler(props: FormProviderProps & FormProviderOnyxProps) { const [log, setLog] = useState(''); // Form consumes data from Onyx, so we initialize Onyx with the necessary data here