Reputation: 6796
If I have a functional component and want to set a value outside of the Formik tag, how would I do that? Below is the code that I have for further clarification.
function XYZScreen() {
const someFunctionWithLogic = () => {
// Set the value of the number field here...
}
return (
<Screen>
<Formik>
<FormField name="number" placeholder="Number" />
</Formik>
</Screen>
);
}
I have reduced the code to as little as possible to simplify the question. The question might not make sense in the context of the question but I think it is clear what I am asking for.
If you need more code, please let me know, I am happy to provide it if needed.
Upvotes: 16
Views: 96206
Reputation: 5119
You can also do this easily with initialValues
<Formik
initialValues={{
"fieldName": this.state.value,
}}
enableReinitialize
...
>
...
</Formik>
Also for functional components, you can do this with useEffect
inside Formik
, set the value using const [value,setValue] = useState()
<Formik
initialValues={{}}
onSubmit={(values, { resetForm }) => _submit(values, resetForm)}
validationSchema={buildValidationSchema(actionItem.fields)}
>
{({ setFieldValue, handleSubmit, values, errors, touched }) => {
useEffect(() => {
setFieldValue("fieldName",value);
}, [value]);
...
</Formik>
Upvotes: 1
Reputation: 131
I just faced similar issue myself, and came up with another solution extending comment from Seth Lutske. You could combine using useFormik
together with manual creation of Formik context. useFormik
is very powerful, but it's downside is that it does not create a context, thus you cannot use useField
or useFormikContext
in child components, and need to manually pass them all the related props (value, onChange, onBlur etc).
What you can do is to create a Formik context object using useFormik
, and pass context down manually using <FormikProvider>
. With this approach, you can continue using all the fields as if they were wrapped in <Formik>
import { useFormik, FormikProvider } from 'formik'
function XYZScreen() {
const formikProps = useFormik({
initialValues,
validationSchema,
onSubmit: yourSubmitFunction,
...etc
})
const someFunctionWithLogic = () => {
// Set the value of the number field here:
formikProps.setFieldValue("number", someNumber)
}
return (
<Screen>
{/* FormikProvider component instead of Formik */}
<FormikProvider value={formikProps}>
<FormField
name="number"
placeholder="Number"
/>
</FormikProvider>
</Screen>
);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
Upvotes: 3
Reputation: 133
The best way to achieve this is by passing setFieldValue function to the function which you want so the code should look like this
function XYZScreen() {
const someFunctionWithLogic = (value, setFieldValue) => {
// Set the value of the number field here...
setFieldValue('price', value)
}
return (
<Screen>
<Formik>
<FormField name="number" placeholder="Number" onChange={(e) => { handleChange(e); someFunctionWithLogic(value, setFieldValue ); }} />
</Formik>
</Screen>
);
}
Reference https://web-brackets.com/discussion/12/how-to-use-setfieldvalue-from-outside-render-function-formik
Upvotes: 2
Reputation: 500
Why don't you try resetting the form using initialProps
?
function XYZScreen() {
const [number, setNumber] = useState()
const someFunctionWithLogic = () => {
// Set the value of the number field here...
setNumber(...)
}
return (
<Screen>
<Formik initialProps={{number}}>
<FormField name="number" placeholder="Number" />
</Formik>
</Screen>
);
}
Another suggestion is to pass that number to the inner component of Formik:
function SomeChild(props) {
const {number, setFieldValue} = props;
useEffect(() => {
setFieldValue('number', number);
}, [number]);
return (
<FormField name="number" placeholder="Number" />
)
}
function XYZScreen() {
const [number, setNumber] = useState()
const someFunctionWithLogic = () => {
// Set the value of the number field here...
setNumber(...)
}
return (
<Screen>
<Formik>
{(props) => <SomeChild number={number} {...props} />
</Formik>
</Screen>
);
}
Upvotes: -1
Reputation: 10752
You can use the useFormik
hook instead:
import { useFormik } from 'Formik'
function XYZScreen() {
const formikProps = useFormik({
initialValues,
validationSchema,
onSubmit: yourSubmitFunction,
...etc
})
const someFunctionWithLogic = () => {
// Set the value of the number field here:
formikProps.setFieldValue("number", someNumber)
}
return (
<Screen>
{/* No need for Formik component */}
<FormField
name="number"
placeholder="Number"
value={formikProps.values.number} // or whatever the value is
onChange={formikProps.handleChange}
/>
</Screen>
);
}
So you're basically setting up all your form props in the function body, and you have access to them there. Formik has a lot of great helper hooks and functions, I highly recommend combing through the docs.
If you really like the Formik
tag, you can keep using it. Just create a Formik
wrapper component, and use your custom logic in a descendant using useFormikContext
:
// FormWrapper.js
const FormWrapper = () => (
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={yourOnSubmitFunction}
>
<SomeChild />
</Formik>
)
// SomeChild.js
const SomeChild = () => {
// returns all values and methods from your Formik tag
const formikProps = useFormikContext()
const someFunctionWithLogic = () => {
formikProps.setFieldValue("number", someNumber)
}
return (
<Screen>
<FormField
name="number"
placeholder="Number"
value={formikProps.values.number} // or whatever the value is
onChange={formikProps.handleChange}
/>
</Screen>
);
}
Upvotes: 28