I use a formik form with forward refs like so
Form.js
import React from "react";
import FormikWithRef from "./FormikWithRef";
const Form = ({
formRef,
children,
initialValues,
validationSchema,
onSubmit
}) => {
return (
<FormikWithRef
validateOnChange={true}
validateOnBlur={true}
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={onSubmit}
ref={formRef}
>
{(props) => <form onSubmit={props.handleSubmit}>{children}</form>}
</FormikWithRef>
);
};
export default Form;
FormikWithRef.js
import React, { forwardRef, useImperativeHandle } from "react";
import { Formik } from "formik";
function FormikWithRef(props, ref) {
let _formikProps = {};
useImperativeHandle(ref, () => _formikProps);
return (
<Formik {...props}>
{(formikProps) => {
_formikProps = formikProps;
if (typeof props.children === "function") {
return props.children(formikProps);
}
return props.children;
}}
</Formik>
);
}
export default forwardRef(FormikWithRef);
I have some tabs, that update an easy-peasy
store state type
, when I select the 2nd tab, I was wanting to update the input value (that initially comes from the store state of value
) with a Formik form, but updating state initialValues
specific to that component that gets passed as initialValues
prop to the Formik
component.
TabsForm.js
import React, { useState, useEffect, useRef } from "react";
import styled from "styled-components";
import { useStoreState } from "easy-peasy";
import Form from "./Form";
import MoneyBox from "./MoneyBox";
const Container = styled.div`
width: 100%;
background-color: #dfdfdf;
`;
const FieldWrapper = styled.div`
padding: 20px 12px;
`;
const TabsForm = () => {
const [initialValues, setInitialValues] = useState();
const type = useStoreState((state) => state.type);
const value = useStoreState((state) => state.value);
const formRef = useRef(null);
const onFormSubmit = async (values) => {
console.log({ values });
};
useEffect(() => {
if (value && type) {
let filterVal = { ...value };
/* here is where I update the input value to be 3000,
the initial values get updated and in the `Form.js` file,
the console log from here also reflects this update,
however, the input field does not update? */
if (type === "Two") filterVal.input = 30000;
setInitialValues(filterVal);
}
}, [value, type]);
useEffect(() => {
// check initialValues has updated
console.log({ initialValues });
}, [initialValues]);
return (
<Container>
{initialValues && type ? (
<Form
initialValues={initialValues}
onSubmit={onFormSubmit}
formRef={formRef}
>
<FieldWrapper>
<MoneyBox name="input" currencySymbol={"£"} />
</FieldWrapper>
</Form>
) : null}
</Container>
);
};
export default TabsForm;
When clicking the 2nd tab;
- The
initialValues
state inTabsForms.js
updates so thatvalue.input
=30000
; - The
initialValues
prop in bothForm.js
andFormikWithRef.js
also reflect thatvalue.input
=3000
- However, the input does not update, using the
useField
hook fromforimk
in theMoneyBox.js
component, thefield
object does not have avalue
of30000
, instead it's whatever the field value was before, why is this?
I have created a CodeSandbox to see all the components used, and console logs to see that the Formik does receive the updated value, but doesn't seem to apply it.
I've been stuck on this for a few days and can't seem to find a solution, any help would be greatly appreciated.
I use a formik form with forward refs like so
Form.js
import React from "react";
import FormikWithRef from "./FormikWithRef";
const Form = ({
formRef,
children,
initialValues,
validationSchema,
onSubmit
}) => {
return (
<FormikWithRef
validateOnChange={true}
validateOnBlur={true}
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={onSubmit}
ref={formRef}
>
{(props) => <form onSubmit={props.handleSubmit}>{children}</form>}
</FormikWithRef>
);
};
export default Form;
FormikWithRef.js
import React, { forwardRef, useImperativeHandle } from "react";
import { Formik } from "formik";
function FormikWithRef(props, ref) {
let _formikProps = {};
useImperativeHandle(ref, () => _formikProps);
return (
<Formik {...props}>
{(formikProps) => {
_formikProps = formikProps;
if (typeof props.children === "function") {
return props.children(formikProps);
}
return props.children;
}}
</Formik>
);
}
export default forwardRef(FormikWithRef);
I have some tabs, that update an easy-peasy
store state type
, when I select the 2nd tab, I was wanting to update the input value (that initially comes from the store state of value
) with a Formik form, but updating state initialValues
specific to that component that gets passed as initialValues
prop to the Formik
component.
TabsForm.js
import React, { useState, useEffect, useRef } from "react";
import styled from "styled-components";
import { useStoreState } from "easy-peasy";
import Form from "./Form";
import MoneyBox from "./MoneyBox";
const Container = styled.div`
width: 100%;
background-color: #dfdfdf;
`;
const FieldWrapper = styled.div`
padding: 20px 12px;
`;
const TabsForm = () => {
const [initialValues, setInitialValues] = useState();
const type = useStoreState((state) => state.type);
const value = useStoreState((state) => state.value);
const formRef = useRef(null);
const onFormSubmit = async (values) => {
console.log({ values });
};
useEffect(() => {
if (value && type) {
let filterVal = { ...value };
/* here is where I update the input value to be 3000,
the initial values get updated and in the `Form.js` file,
the console log from here also reflects this update,
however, the input field does not update? */
if (type === "Two") filterVal.input = 30000;
setInitialValues(filterVal);
}
}, [value, type]);
useEffect(() => {
// check initialValues has updated
console.log({ initialValues });
}, [initialValues]);
return (
<Container>
{initialValues && type ? (
<Form
initialValues={initialValues}
onSubmit={onFormSubmit}
formRef={formRef}
>
<FieldWrapper>
<MoneyBox name="input" currencySymbol={"£"} />
</FieldWrapper>
</Form>
) : null}
</Container>
);
};
export default TabsForm;
When clicking the 2nd tab;
- The
initialValues
state inTabsForms.js
updates so thatvalue.input
=30000
; - The
initialValues
prop in bothForm.js
andFormikWithRef.js
also reflect thatvalue.input
=3000
- However, the input does not update, using the
useField
hook fromforimk
in theMoneyBox.js
component, thefield
object does not have avalue
of30000
, instead it's whatever the field value was before, why is this?
I have created a CodeSandbox to see all the components used, and console logs to see that the Formik does receive the updated value, but doesn't seem to apply it.
I've been stuck on this for a few days and can't seem to find a solution, any help would be greatly appreciated.
Share Improve this question asked Aug 6, 2020 at 16:29 mcclosamcclosa 1,4559 gold badges34 silver badges78 bronze badges3 Answers
Reset to default 20If you want the value of the input to change when you change initialValues
, you need to pass to the Formik
component the prop enableReinitialize
as true
.
So, what you need to change in your code is in TabsForm.js
pass to your Form
component the prop enableReinitialize
<Form
enableReinitialize
initialValues={initialValues}
onSubmit={onFormSubmit}
formRef={formRef}
>
<FieldWrapper>
<MoneyBox name="input" currencySymbol={"£"} />
</FieldWrapper>
</Form>
And in your Form.js
pass that prop to the Formik
component
const Form = ({
formRef,
children,
initialValues,
validationSchema,
onSubmit,
enableReinitialize
}) => {
return (
<FormikWithRef
enableReinitialize={enableReinitialize}
validateOnChange={true}
validateOnBlur={true}
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={onSubmit}
ref={formRef}
>
{(props) => <form onSubmit={props.handleSubmit}>{children}</form>}
</FormikWithRef>
);
};
I'm not quite sure how you business logic should work, but here is a working example with the changes above.
( Solution CodeSandbox)
It's called initialValues
, so why you expect it to update the form values when you change it? (however you can ask it to do so by using enableReinitialize
prop, as @Vencovsky mentioned in another answer.)
For binding your desired value (value.input
in the easy-peasy
store) to formik input, you can use:
const [field, meta, helpers] = useField(props);
useEffect(() => {
helpers.setValue(value.input)
}, [value])
it will update the value of the formik input field every time the value
in store changes.
and for changing the value of the state in store, you can use the way you did it for setting tabs. (using easy-peasy
store.)
Run It On CodeSandbox
On line 49 in Tabs.js
, it updates the value when a tab is clicked.
On line 19 in Input.js
, it binds the input value to your store state.
enableReinitialize={true} prop will do the trick for you