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.
- To update by state, set your
result
to
const [result, setResult] = useState({
monthDuration: 0,
amountFinanced: <any number between 10000 to 100000>,
});
- Then set
initialValues
inFormik
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.