Reputation: 2948
I have this simple example of Formik where i have a simple input. When i run the page i see in the console that it renders twice. The formik package is exactly the same as the first message. Why it renders twice if there is nothing changed?
const SignupForm = () => {
const [data, setData] = useState({
firstName: "",
lastName: "",
email: "",
});
return (
<Formik
initialValues={data}
enableReinitialize
validateOnBlur={false}
validateOnChange={false}
onSubmit={(values, { setSubmitting }) => {
}}
>
{(formik) => {
console.log(formik);
return (
<form onSubmit={formik.handleSubmit}>
<label htmlFor="firstName">First Name</label>
<input
id="firstName"
type="text"
{...formik.getFieldProps("firstName")}
/>
{formik.touched.firstName && formik.errors.firstName ? (
<div>{formik.errors.firstName}</div>
) : null}
</form>
);
}}
</Formik>
);
};
Upvotes: 0
Views: 696
Reputation: 4600
That is happening due to enableReinitialize
property.
Formik itself has a few useEffect
s inside of it, and a formikReducer
. So when you pass enableReinitialize
- formikReducer is called 2 times:
payload: {}, type: "SET_ERRORS"
payload: {}, type: "SET_TOUCHED"
Which is happening due to folowing useEffects inside of the source codes:
React.useEffect(function () {
if (enableReinitialize && isMounted.current === true && !isEqual(initialErrors.current, props.initialErrors)) {
initialErrors.current = props.initialErrors || emptyErrors;
dispatch({
type: 'SET_ERRORS',
payload: props.initialErrors || emptyErrors
});
}
}, [enableReinitialize, props.initialErrors]);
React.useEffect(function () {
if (enableReinitialize && isMounted.current === true && !isEqual(initialTouched.current, props.initialTouched)) {
initialTouched.current = props.initialTouched || emptyTouched;
dispatch({
type: 'SET_TOUCHED',
payload: props.initialTouched || emptyTouched
});
}
}, [enableReinitialize, props.initialTouched]);
And those if
s are passed due to the initialTouched
and initialErrors
are initialized inside of the Formik with this:
var initialErrors = React.useRef(props.initialErrors || emptyErrors);
var initialTouched = React.useRef(props.initialTouched || emptyTouched);
So initial
values are equal to empty
ones which are {}
. But inside of the if
they have !isEqual(initialErrors.current, props.initialErrors))
for example, so comparison between {}
and undefined
is passed and we are going inside of the if
body and updating the Formik internal state. That is what is causing an additional rerender.
So if you pass the following props to Formik component - console.log will be executed only once
initialErrors={{}}
initialTouched={{}}
Now about how to collect that information:
node_modules/formik/dist/formik.cjs.development.js
and inject some logging code inside of the formikReducer
, simple console.log<Formik>
import it from modified development js file. import { Formik } from "formik/dist/formik.cjs.development";
Formik version: 2.2.9
Upvotes: 2