Tengo este componente simple Iniciar sesión:
function Login() { const [isFormValidState, setIsFormValidState] = React.useState(false); const [credentialState, setCredentialState] = React.useState(); function getFormErrors(errors: any, dirty: boolean) { setIsFormValidState(!Object.keys(errors).length && dirty); } function getFormValues(values: any) { setCredentialState(values); } function doAction() { //credentialState rest call... } return ( <View> <Text>Login</Text> <UserCredentialForm getFormValues={getFormValues} getFormErrors={getFormErrors}/> <Button title='Entra' disabled={!isFormValidState} onPress={doAction}/> </View> ); }Que llama UserCredentialForm:
export default function UserCredentialForm({ getFormValues, getFormErrors }) { [...] return ( <Formik innerRef={formRef} validationSchema={formSchema} initialValues={state.form} onSubmit={() => { }}> {({ handleChange, values, touched, errors, dirty }) => { getFormValues(values); getFormErrors(errors, dirty); return <React.Fragment> // <TextInput/>.... </React.Fragment> }} </Formik> ); [...] }Mientras navego en mi aplicación, tengo este error:
Reaccionar nativo no puede actualizar el inicio de sesión de un componente mientras se procesa un componente Formik diferente.
Luego me señala el error en setCredentialState dentro del controlador getFormValues en el componente de inicio de sesión. Resolví esto usando una referencia en lugar de un estado, pero el problema en sí no está resuelto para mí.
¿Qué sucede si necesito actualizar la vista de mi componente principal después de un evento secundario?
Creo que tienes un ciclo ilimitado de renderizado,
establece State by getFormValues y el componente de inicio de sesión se vuelve a renderizar, hace que UserCredentialForm se vuelva a renderizar también, por lo que llama a getFormValues una y otra vez
Puede llamar a getFormValues(values) en un gancho useEffect después de actualizar los valores de formik
Un ejemplo simple puede ser como
Formulario de credencial de usuario:
// Formik x React Native example import React from 'react'; import { Button, TextInput, View } from 'react-native'; import { Formik } from 'formik'; export const MyReactNativeForm = ({onSubmit}) => ( <Formik initialValues={{ email: '' }} onSubmit={values => onSubmit(values)} > {({ handleChange, handleBlur, handleSubmit, values }) => ( <View> <TextInput onChangeText={handleChange('email')} onBlur={handleBlur('email')} value={values.email} /> <Button onPress={handleSubmit} title="Submit" /> </View> )} </Formik> );uso como
function Login() { function doAction(values) { console.log(values); //credentialState rest call... } return ( <View> .... <UserCredentialForm onSubmit={doAction} /> .... </View> ); }El motivo de ese error es que llamas a setState dentro de render() . La llamada getFormValues(values) , que establece el estado de credentialState , se llama dentro del render.
Cuando se establece el estado, el componente de inicio de Login se vuelve a representar, recreando así una nueva función de getFormValues . Como esto se usa como accesorio de UserCredentialForm , también hace que ese componente se vuelva a procesar, lo que hace que el accesorio de procesamiento dentro de Formik a llamar, lo que llama a getFormValues provoca el cambio de estado, lo que provoca un bucle infinito.
Una solución que puede probar es agregar useCallback a las dos funciones, lo que evita que tengan nuevas identidades después de que cambie el estado y, en consecuencia, cambie los accesorios, creando así una reproducción infinita.
function Login() { const [isFormValidState, setIsFormValidState] = React.useState(false); const [credentialState, setCredentialState] = React.useState(); const getFormErrors = useCallback(function getFormErrors(errors: any, dirty: boolean) { setIsFormValidState(!Object.keys(errors).length && dirty); }, []); const getFormValues = useCallback(function getFormValues(values: any) { setCredentialState(values); }, []); function doAction() { //credentialState rest call... } return ( <View> <Text>Login</Text> <UserCredentialForm getFormValues={getFormValues} getFormErrors={getFormErrors}/> <Button title='Entra' disabled={!isFormValidState} onPress={doAction}/> </View> ); } Sin embargo, todavía hay un problema y es que la identidad de los values puede no ser estable y al establecerlo en el estado, seguirá provocando la repetición. Lo que quiere hacer es decirle a UserCredentialForm que no vuelva a procesar incluso cuando ese estado cambie, y dado que el estado no se usa como accesorio en UserCredentialForm , puede hacerlo con React.memo .
export default React.memo(function UserCredentialForm({ getFormValues, getFormErrors }) { [...] return ( <Formik innerRef={formRef} validationSchema={formSchema} initialValues={state.form} onSubmit={() => { }}> {({ handleChange, values, touched, errors, dirty }) => { getFormValues(values); getFormErrors(errors, dirty); return <React.Fragment> // <TextInput/>.... </React.Fragment> }} </Formik> ); [...] })