Home > Blockchain >  Clearing Formik errors and form data - React Native
Clearing Formik errors and form data - React Native

Time:03-29

Im using Formik and was wondering how I go about clearing the errors and form values when leaving a screen

For example, a user tries to submit the form with no values and the errors are displayed enter image description here

When the user then navigates to a different screen and then comes back those errors are still present. Is there a way to clear these? Can I access Formik methods within a useEffect hook as an example?

This is my implementation so far

export const SignIn = ({route, navigation}) => {

  const formValidationSchema = Yup.object().shape({
    signInEmail: Yup.string()
      .required('Email address is required')
      .email('Please provide a valid email address')
      .label('Email'),
     signInPassword: Yup.string()
      .required('Password is required')
      .label('Password'),
  });

  const initialFormValues = {
    signInEmail: '',
    signInPassword: '',
  };

  return (
    <Formik
      initialValues={initialFormValues}
      validationSchema={formValidationSchema}
      onSubmit={(values, formikActions) => {
      handleFormSubmit(values);
    }}>
    {({handleChange, handleSubmit, errors}) => (
      <>
        <SignInForm
          messages={errors}
          navigation={navigation}
          handleFormSubmit={handleSubmit}
        />
      </>
    )}
   </Formik>
  )

}

CodePudding user response:

This should be possible with the useFocusEffect hook from react navigation. It gets triggered on first time and every time the screen comes in focus.

useFocusEffect(
    React.useCallback(() => {
      // set Formik Errors to empty object
      setErrors({});
      // Since your clearing errors, you should also reset the formik values
      setSignInEmail('');
      setSignInPassword('');

      return () => {}; // gets called on unfocus. I don't think you need this
    }, [])
  );

CodePudding user response:

The problem here is that a screen does not get unmounted if we navigate to a different screen and the initial values of a Formik form will only be set on screen mount.

I have created a minimal example with one field and I navigate whenever the submit button is executed to a different Screen in a Stack.Navigator. Notice that the onSubmit function is usually not fired if there are errors in your form fields. However, since I wanted to provide a quick test, I navigate by hand by calling the function directly.

If we navigate back by pressing the onBack button of the navigator, the form fields will be reseted to the default values and all errors will be reseted automatically.

We can trigger this by hand using the Formik innerRef prop and a focus listener.

For testing this, you should do the following.

  1. Type something, and remove it. Notice the error message that is rendered below the input field.
  2. Navigate to the screen using the submit button.
  3. Go back.
  4. Expected result: no error message.
  5. Type something. Expected result: no error message.
  6. Navigate on submit.
  7. Go back.
  8. Expected result: no error message, no content in field.

In principal, this will work with every navigator, e.g. changing a Tab in a Tab.Navigator and it will reset both, the errors and the field's content.

The key part is given by the following code snippet.

const ref = useRef(null)
  const initialFormValues = {
    signInEmail: '',
    signInPassword: '',
  };

  React.useEffect(() => {
    const unsubscribe = navigation.addListener('focus', () => {
      if (ref?.current) {
        ref.current.values = initialFormValues
        ref.current.setErrors({})
        console.log(ref.current)
      }
    });
    return unsubscribe;
  }, [navigation, initialFormValues]);

return (
    <Formik
      innerRef={ref}
      isInitialValid={true}
      initialValues={initialFormValues}
      validationSchema={formValidationSchema}
      onSubmit={(values) => {
          console.log("whatever")
    }}>

...

The full code is given as follows.

import React, { useRef} from 'react';

import { StyleSheet, Text, View,TextInput, Button } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { Formik } from 'formik';

import * as Yup from 'yup';


export const SignIn = ({route, navigation}) => {

  const formValidationSchema = Yup.object().shape({
    signInEmail: Yup.string()
      .required('Email address is required')
      .label('Email'),
  });

  const ref = useRef(null)
  const initialFormValues = {
    signInEmail: '',
    signInPassword: '',
  };

  React.useEffect(() => {
    const unsubscribe = navigation.addListener('focus', () => {
      if (ref?.current) {
        ref.current.values = initialFormValues
        ref.current.setErrors({})
        console.log(ref.current)
      }
    });
    return unsubscribe;
  }, [navigation, initialFormValues]);


  function handleFormSubmit(values) {
    navigation.navigate("SomeScreen")
  }

  return (
    <Formik
      innerRef={ref}
      isInitialValid={true}
      initialValues={initialFormValues}
      validationSchema={formValidationSchema}
      onSubmit={(values) => {
          console.log("whatever")
    }}>
    {({handleChange, handleSubmit, errors, values}) => (
      <>
        <View>
         <TextInput
           style={{height: 30}}
           placeholder={"Placeholder mail"}
           onChangeText={handleChange('signInEmail')}
           value={values.signInEmail}
         />
         {errors.signInEmail ?
            <Text style={{ fontSize: 10, color: 'red' }}>{errors.signInEmail}</Text> : null
         }
         <Button onPress={() => {
           handleSubmit()
           handleFormSubmit()}} title="Submit" />
       </View>
      </>
    )}
   </Formik>
  )

}

export function SomeOtherScreen(props) {
  return <></>
} 

const Stack = createNativeStackNavigator();

export default function App() {
    return (
        <NavigationContainer>
            <Stack.Navigator>
              <Stack.Screen name="Form" component={SignIn} />
              <Stack.Screen name="SomeScreen" component={SomeOtherScreen} />
            </Stack.Navigator>
        </NavigationContainer>
    );
}

const styles = StyleSheet.create({
    container: {
        flex: 1,
        backgroundColor: '#fff',
        alignItems: 'center',
        justifyContent: 'center',
    },
});
  • Related