Listen to Formik values changes
Asked Answered
D

4

5

I've a formik form in a component that I'd want to reuse. In order to do that I need to listen to the form values changes in order to call two functions, getFormValues and getFormErrors, that will be passed by the parent component:

import React from "react";
import * as Yup from 'yup';
import { Formik } from 'formik';
import CheckBox from "@react-native-community/checkbox";
import { Text, TextInput } from "react-native";
import { errorStyle } from "../../styles/form.styles";

export default function UserCredentialForm({getFormValues, getFormErrors}) {
  const formSchema = Yup.object().shape({
    email: Yup.string().email()
      .required('Required'),
    password: Yup.string()
      .test('lenght', 'min 5chr max 10chr', (val = '') => val.length > 4 && val.length < 11)
      .required('Required'),
  });
  const [state, setState] = React.useState({
    form: {
      email: '',
      password: '',
    }
  });
  const formRef = React.useRef({} as any);

  React.useEffect(() => {    
    console.log('formref', formRef.current.values);
    getFormValues(formRef.current.values);
    getFormErrors(formRef.current.errors);
  }, [formRef.current.values, formRef.current.errors]);

  return (
    <Formik innerRef={formRef} validationSchema={formSchema} initialValues={state.form} onSubmit={() => {}}>
      {({ handleChange, values, touched, errors }) => (
        <React.Fragment>
          {errors.password && touched.password ? <Text>{errors.email}</Text> : null}
          <TextInput
            style={errors.email && touched.email ? errorStyle.input : null}
            keyboardType='email-address'
            onChangeText={handleChange('email')}
            value={values.email}
            placeholder="email"
          />
          {errors.password && touched.password ? <Text>{errors.password}</Text> : null}
          <TextInput
            style={errors.password && touched.password ? errorStyle.input : null}
            onChangeText={handleChange('password')}
            value={values.password}
            placeholder="password"
            secureTextEntry={true}
          />
        </React.Fragment>
      )}
    </Formik>
  );
}

As you can see I'm using useEffect hooks linked to the form ref values, but it get called only once at form first render. What am I missing? How can I subscribe to all values changes?

Dissident answered 23/1, 2022 at 19:28 Comment(0)
D
5

Got a solution here:

https://mcmap.net/q/1674935/-update-another-component-when-formik-form-changes

I need to move my hooks inside the form

<Formik
    initialValues={initialValues}
    ...
  >
    {props => {
      getFormErrors(props.errors); // store values in state 'formValues'
      getFormValues(props.values); // or use any function to get values like this
      return (
        <form onSubmit={props.handleSubmit}>
Dissident answered 24/1, 2022 at 17:29 Comment(0)
M
2

Use useFormikContext hook to get values. You shouldn't be using ref inside useEffect because mutating the ref.current won't trigger a render. Here is the implementation of useFormikContext hook

https://formik.org/docs/api/useFormikContext

Masquer answered 23/1, 2022 at 22:49 Comment(1)
useFormikContext works from parento to child, I need something to comunicate from child to parentDissident
O
0

here is what worked for me: you try to declare the formik context inside of the Formik component, as mentioned in the docs:

Thus, this hook will only work if there is a parent Formik React Context from which it can pull from.

Then you just update the values directly when other specific values change by implementing a useEffect, here is my code:

      <Formik
    initialValues={inits}
    enableReinitialize={true}
    validationSchema={validationSchema}
    onSubmit={handleFormSubmit}
  >
    {(props) => {
      const {values, setFieldValue} =props;

      useEffect(() => {
        if (values?.A && values?.B) {
          setFieldValue('total', values.A + values.B)
        }
      }, [values?.A, values?.B, setFieldValue])

      return (
        <Form>
          <div className='d-flex flex-column'>
            ...
          </div>
        </Form>
      )
    }}
  </Formik>
Outride answered 8/4 at 22:6 Comment(0)
T
0

You can do

 onChange={(e) => {
   handleChange(e);
   console.log("Value changed");
 }}
Tortious answered 8/10 at 9:5 Comment(1)
Please explain how this addresses the issue. What have you changed? Code-only answers are not good answersEva

© 2022 - 2024 — McMap. All rights reserved.