Update another component when Formik form changes
Asked Answered
U

3

6

There is a basic Formik form:

<Formik
      initialValues={{ email: '', color: 'red', firstName: '' }}
      onSubmit={(values, actions) => {
        setTimeout(() => {
          alert(JSON.stringify(values, null, 2));
          actions.setSubmitting(false);
        }, 1000);
      }}
      render={props => (
        <form onSubmit={props.handleSubmit}>
          <Field type="email" name="email" placeholder="Email" />
          <div>other inputs ... </div>
          <button type="submit">Submit</button>
        </form>
      )}
    />

When any input in it changes (not submits, but changes) - I need to update another component that is outside of <Formik />. The "outside" component should receive all form data.

Is there some way to do it without adding separate change handler for each individual input of a form? Or the solution is to try to insert "outside" component inside <Formik />?

Unscramble answered 23/5, 2019 at 4:44 Comment(0)
G
10

Formik provides values object which you can make use of to get values outside.

const App = () => {
  const initialValues = { email: '', color: 'red', firstName: '' }

  const [formValues, setformValues] = useState(initialValues);

  const getFormData = values => {
    // access values here
  };
  return (
    <div>
      <h1>Formik take values outside</h1>
      <Formik
        initialValues={initialValues}
        ...
      >
        {props => {
          setformValues(props.values); // store values in state 'formValues'
          getFormData(props.values); // or use any function to get values like this
          return (
            <form onSubmit={props.handleSubmit}>
            ...

Working demo in codesandbox here

Granophyre answered 23/5, 2019 at 6:19 Comment(0)
R
1

You should not call setState during the render cycle of a component, as has been suggested:

<Formik
  render={props => (
    setformValues(props.values) // store values in state 'formValues'
    // ...
  )}
/>

Rather, you should call setState as a side-effect. A more verbose, yet better solution would be:

// handle when form values change
const FormikOnChange = ({ onChange }) => {
  const { values } = useFormikContext()

  useEffect(
    () => {
      onChange(values)
    },
    [values]
  )

  return null
}

const App () => {
  const [formValues, setformValues] = useState(initialValues)

  // ...do stuff with form values...

  return (
    <Formik
      render={props => (
        <FormikOnChange onChange={setformValues} /> // store values in state 'formValues'
        // ...
      )}
    />
  )
}
Resign answered 20/3, 2023 at 12:3 Comment(0)
C
-1
export const LoginForm: React.FC<Values> = () => {


  const initialValues = { user: "", password: "" };
  const [formValues, setformValues] = React.useState(initialValues);


  return (

              <div>{formValues.user}</div>

              <Formik
                initialValues={initialValues}
                validationSchema={ValidationSchema}
                onSubmit={(values, { setSubmitting, resetForm }) => {

                  setTimeout(() => {
                    //alert(JSON.stringify(values, null, 2));
                    resetForm();
                    setSubmitting(false);
                    setformValues(values);
                  }, 500);
                }}
              >
                {({
                  values,
                  errors,
                  touched,
                  handleChange,
                  handleBlur,
                  handleSubmit,
                  isSubmitting,
                }) => {
                  return (
   <>
                            <TextField
                              label="Usuario"
                              name="user"
                              value={values.user}
                              onChange={handleChange}
                              onBlur={handleBlur}
                              fullWidth
                              color={touched.user && errors.user ? "primary" : "secondary"}
                            />
                            <Error touched={touched.user} message={errors.user} />
                          </> 
                        <div className="pane-form__submit">
                          <Button
                            className={classes.customHoverFocus}
                            variant="contained"
                            type="submit"
                            disabled={isSubmitting}
                            label="CONTINUAR"
                          >Continuar</Button>
                        </div>
                      </Form>
                  )
                }}
              </Formik>


    </>
  );
};
Clique answered 30/3, 2020 at 4:18 Comment(1)
Thank you for this code snippet, which might provide some limited, immediate help. A proper explanation would greatly improve its long-term value by showing why this is a good solution to the problem and would make it more useful to future readers with other, similar questions. Please edit your answer to add some explanation, including the assumptions you’ve made.Ake

© 2022 - 2024 — McMap. All rights reserved.