Search code examples
javascriptreact-nativedeep-linkingformik

Formik State returns OLD NOT the RECENT or CURRENT values after Linking Event Listener is called


I'm trying to create a registration flow in REACT-NATIVE. The process is: fill up form > validate and submit form > open a URL that returns a deeplink > catch the deeplink > register the user using form values and deeplink params

The problem is when listener gets called, and I've tried to use formik form values inside it, it returns the initialValue of Formik, not the updated CURRENT value.

const formik = useFormik({
    initialValues: {
      username: '',
      email: '',
      password: ''
    },
    onSubmit: (values) => {
      Linking.openURL(URL);
    },
    validationSchema: getValidation(), // Yup.object
  });

useEffect(() => {
    Linking.addEventListener('url', handleOpenUrl);
    return () => {
      Linking.removeListener('url', handleOpenUrl);
    };
  }, []);

const handleOpenUrl = (event) => {
    var code = event.url.split('//')[1].split('/')[1];
  
    if (code !== 'error) {
      console.log(code , formik.values); // returns initialValue
      // onRegister(formik.values, code )
    }
  };

I call formik.handleSubmit() to validate the data and call onSubmit


Solution

  • For anyone who encountered the same problem or a similar one, here's why it happened and what I did to fix it.

    Problem/Cause:

    Event listeners will only be aware of the state of the component at the initial render. Because the event listeners are not updated when state changes, they will not be aware of changes taking place. This is an issue to React's Event Listeners, not just in useFormik.

    Solution:

    useRef hook, it can access real-time state values

    Code:

    const formik = useFormik({
      initialValues: {
        username: '',
        email: '',
      }
      onSubmit: (values) => {
        Linking.openURL(URL_TO_OPEN);
      },
      validationScheme: someValidationScheme(), // Yup.object
    });
    
    // just like other hook, you need to put an initial value, use the same object used in useFormik
    const formValueRef = useRef(initialValues);
    
    // useRef.current - stores the CURRENT value you passed, and can be accessed anytime
    const setFormValues = (values) => {
      formValueRef.current = values;
      formik.setValues(values);
    };
    
    useEffect(() => {
      Linking.addEventListener('url', handleOpenUrl);
      return () => {
        Linking.removeListener('url', handleOpenUrl);
      };
    }, []);
    
    
    const handleOpenUrl = (event) => {
      var code = event.url.split('//');
    
      if (code !== 'error) {
        //this NOW RETURNS the CURRENT VALUE of formik that is passed to formValueRef.current
        console.log(code, formValueRef.current.username, formValueRef.current.email); 
      }
    }