Home > Software engineering >  Formik returns old values state
Formik returns old values state

Time:02-14

I'm having this trouble with async state management with Formik. I need to dynamically show a result, according to input changes, but I get every time the old state. Do you know a workaround? I need the result state to be updated with changes in the Field's component.

Here's my code:

 function Simulator() {
  const [result, setResult] = useState(undefined);

  const LeasingSchema = Yup.object().shape({
    monthDuration: Yup.number()
      .min(6, "6 months minimum")
      .max(48, "48 months maximum")
      .required("Required"),
    amountFinanced: Yup.number()
      .min(10000, "The minimum finance is 10.000€")
      .max(100000, "The maximum finance is 100.000€")
      .required("Required")
  });

  const handleCalculation = (values, errors) => {
    const { monthDuration, amountFinanced } = values;

    if (monthDuration && amountFinanced && Object.keys(errors).length === 0) {
      //Round number
      const roundedValue = parseFloat(amountFinanced / monthDuration).toFixed(
        2
      );
      return setResult(roundedValue);
    } else {
      return setResult(undefined);
    }
  };

  return (
    <div className={styles.leasing}>
      <h1>Leasing simulator:</h1>

      <Formik
        initialValues={{
          monthDuration: "",
          amountFinanced: ""
        }}
        validationSchema={LeasingSchema}
        onSubmit={(values) => console.log(values)}
      >
        {({ errors, touched, values, handleChange }) => (
          <Form>
            <label htmlFor="form-monthly-duration">Monthly duration:</label>
            <Field
              type="number"
              name="monthDuration"
              placeholder="Months"
              id="form-monthly-duration"
              onChange={(e) => {
                handleChange(e);
                handleCalculation(values, errors);
              }}
            />
            {result && (
              <>
                <label htmlFor="result">Monthly fee:</label>
                <p>{result}</p>
              </>
            )}
            <button type="submit">Submit</button>
          </Form>
        )}
      </Formik>
    </div>
  );
}

export default Simulator;

CodePudding user response:

The values in handleCalculation are never updated because you are using a controlled component. Form data must be updated by state.

  1. To update by state, set your result to
const [result, setResult] = useState({
    monthDuration: 0,
    amountFinanced: <any number between 10000 to 100000>,
});
  1. Then set initialValues in Formik component to
initialValues={result}

Full code here:

const Simulator = () => {
    const [result, setResult] = useState({
        monthDuration: 0,
        amountFinanced: 10000,
    });

    const LeasingSchema = Yup.object().shape({
        monthDuration: Yup.number()
            .min(6, "6 months minimum")
            .max(48, "48 months maximum")
            .required("Required"),
        amountFinanced: Yup.number()
            .min(10000, "The minimum finance is 10.000€")
            .max(100000, "The maximum finance is 100.000€")
            .required("Required")
    });

    const handleCalculation = (values, errors) => {
        const { monthDuration, amountFinanced } = values;

        if (monthDuration && amountFinanced && Object.keys(errors).length === 0) {
            setResult({
                monthDuration: monthDuration,
                amountFinanced: amountFinanced,
            });
        }
    };

    return (
        <div>
            <h1>Leasing simulator:</h1>

            <Formik
                initialValues={result}
                validationSchema={LeasingSchema}
                onSubmit={(values) => console.log(values)}
            >
                {({ errors, values, handleChange, handleSubmit }) => (
                    <Form onSubmit={handleSubmit}>
                        <label htmlFor="form-monthly-duration">Monthly duration:</label>
                        <Field
                            type="number"
                            name="monthDuration"
                            placeholder="Months"
                            id="form-monthly-duration"
                            onChange={(e) => {
                                handleChange(e);
                                handleCalculation(values, errors);
                            }}
                        />
                        {(result.monthDuration || result.amountFinanced) && (
                            <>
                                <label htmlFor="result">Monthly fee:</label>
                                <p>{(values.amountFinanced / values.monthDuration).toFixed(2)}</p>
                            </>
                        )}
                        <button type="submit">Submit</button>
                    </Form>
                )}
            </Formik>
        </div>
    );
};

export default Simulator;

You can also remove return when setting state as it is unnecessary. You can find more in the React lifecycle docs.

  • Related