Update Formik's state from a button onClick - reactjs

I have a Formik form and when I click a button I want to update the form's state.
function updateForm(){
//Update a product in the form's state
}
<Button onClick={updateForm}>Update Product</>
<Formik
initialValues={{ products: {} }}
>
{({values}) = (
)}
</Formik>
I tried to update the initialValues but that will reset any current changes in the form's state.
I could copy the Form's state into a local setState and loop that back into the initialValues but that feels overkill, as Formik does that already.
I thought useFormik might work, but the docs don't expand how this might work...?

Given the helpful comments from #aditaya81070, I eventually landed on this solution...
I also had to refactor my solution a little, because I had two Forms on the page and in order to use setFieldValue I had to nest both Forms within the <Formik> element, so they could communicate easily (they can still have their own state separately). But don't nest them within themselves as that's not allowed.
So this is fine:
<Formik>
<Form />
<Form />
</Formik>
but this is not...
<Formik>
<Form>
<Form />
</Form>
</Formik>
Then I just pass in the setFieldValue into the callback of the button...easy :)
function updateForm(setFieldValue){
//Update a product in the form's state
setFieldValue('product.price'), 1.50);
}
<Formik
initialValues={{ products: {} }}
>
{({values, setFieldValue, validateForm}) = (
<button onClick={() =>
//This is the import bit, to get the context.
updateForm(setFieldValue)
}>
Update Product
<button/>
)}
</Formik>

Related

Hoisting Formik values to higher level state

In a Formik component, I need one of the values from the values prop to be available outside of the component. Preferably bound to a state variable.
<Formik
initialValues={initialValues}
validationSchema={LocationAddressSchema}
onSubmit={onSubmit}
>
{({ isSubmitting, isValid, values }) => (
<Form>
How can I take what comes back from values.name for example and set it to a state variable so that every time the value of values.name changes the state will re render holding that new value
Did you try this way?
<Formik ...>
...
<input type="text" onChange={props.handleChange} ...>
...
</Formik>

How to access state from components to the parent component

I have a form in which all input, select tags are separate components and each component but the submit button is in the form it self like-
<form>
<InputComp1 />
<InputComp2 />
<Select1 />
<Select2 />
<button type='submit' value='Register'/>
</form>
So how do I collect all state from various components and when user clicks on the submit the values get submitted.?
Is this approach while dealing with forms right? or should I manage state of all tags in the same component?
Manage the state of all inputs/selects in this component. You can pass values and handler functions to the inputs using props.
There is no "right" approach, the answer depends on the context.
You can have form as a controlled component, where you manage the state of all tags (while passing callbacks down the tree) as you suggested and as mentioned in docs.
Or, you can have it as uncontrolled component, for example:
const Input = ({ name }) => {
return <input name={name} />;
};
const Component = () => {
return (
<>
<form
onSubmit={(e) => {
e.preventDefault();
const data = new FormData(e.target);
const entries = data.entries();
for (let entry of entries) {
console.log(entry);
}
}}
>
<Input name="username" />
<Input name="email" />
<input type="submit" value="Submit" />
</form>
</>
);
};
See controlled vs uncontrolled components.
Yes you should manage the state in the parent component itself and pass the onchange handler and value of that field as props inside the child components to update the value of the form fields.
Solution #1 would be "manage state react way". With this you should store state you need to share between components somewhere in their common ancestor. In your case it would be component that holds Form
Solution #2 applicable only if you use real form and form controls. Handle 'submit' event from the form and get all you need to submit from form data.
Solution #3 applicable only if you use some sort of "state manager". Follow instructions and best practices of the library you use.
In some cases you can mix and match that solutions. For example I still recommend to handle 'submit' event on form regardless of solution.
there is a concept of state lifting in react:
create a controlled form here and for every child, component pass a function to get the data from child components to parent one. by doing this you can submit all the values once.
here is the example
import React, {useState} from 'react';
const ChildInput = ({onChange, id}) => {
return(
<input
key={id}
type="text"
placeholder="enter name"
onChange={onChange}
/>
)
}
const Parent = () => {
const [name, setName] = useState('');
const onSubmit =(e)=>{
e.preventDefault();
// append your all data here just like child component
data = {name}
}
return(
<form onSubmit={onSubbmit}>
<ChildInput onChange={()=>setName(e.target.value)} id="name" />
<button type="submit" value="submit"/>
</form>
)}
for more information check this one: https://reactjs.org/docs/glossary.html#controlled-vs-uncontrolled-components

Trouble Understanding Syntax for Props Access in Formik

In the example given for <Formik /> here we can gain access to the Formik context props in children with this code:
import React from 'react';
import { Formik } from 'formik';
const BasicExample = () => (
<div>
<h1>My Form</h1>
<Formik
initialValues={{ name: 'jared' }}
onSubmit={(values, actions) => {
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
actions.setSubmitting(false);
}, 1000);
}}
>
{props => (
<form onSubmit={props.handleSubmit}>
<input
type="text"
onChange={props.handleChange}
onBlur={props.handleBlur}
value={props.values.name}
name="name"
/>
{props.errors.name && <div id="feedback">{props.errors.name}</div>}
<button type="submit">Submit</button>
</form>
)}
</Formik>
</div>
);
I'm having an issue understanding what's happening here:
>
{props => (
Where is this 'props' coming from and what sort of syntax is this. I have a feeling the syntactic sugar of the <Formik /> is making this harder to wrap my head around.
Thanks.
The props you get there are known as render props, please refer to this link for official documentation.
Render Props are a way of passing props down to the children
Using this style pattern, we render children as a function & we can pass parameters to that
In the same way, <Formik/> passding down some renderProps down the children
you can refer to this <Formik/> component code, if you want to see how the props are being passed & how the children are being rendered
Please refer to this article with detailed explanation of render props
Please refer to this code sandbox for live example
Remember that in JSX anything inside two tags is the children.
E.g
<div>
I’m the child
</div>
Well what if you make the children a function?
<div>
{() => { console.log('The children has been executed')}
</div>
Well, if the wrapping component expects it’s children to be a function (the same as an onClick prop is a function) it can call it and pass data when it does.
This is a way of using data from a child component as input to determine what to render.
Here formik is one such component - and it will pass the form props to the children function. This is also known as render props.

Formik checkbox won't re-render

I am using Formik library and have a simple form with one checkbox that I would like to submit on change:
<Formik
initialValues={{ toggle: false }}
validateOnChange={false}
validateOnBlur={false}
onSubmit={(values, { validateForm }) => {
validateForm().then(_errors => {
console.log(values);
});
}}
>
{({ values, handleChange, handleSubmit }) => (
<div>
<form onChange={handleSubmit}>
<label>
Toggle
<input
name="toggle"
type="checkbox"
checked={values.toggle}
onChange={handleChange}
/>
</label>
</form>
</div>
)}
</Formik>
For some reason, it looks like the input is not being re-render after every click, only after every second click. As a result checkbox is not being update - you have to click twice for it to change (onChange event only fires every second time)
I can force it to re-render by adding key but it's a hack.
Here's the sandbox: https://codesandbox.io/s/formik-checkbox-issue-ew65e
Your problem is that you are trying to submit the form at every change.
Idealy you should debounce this behaviour (using lodash maybe ?) :
import _ from 'lodash'
<form onChange={_.debounce(handleSubmit, 300)}>
Alternatively, if you don't use lodash, you can make the call to handleSubmit asynchronous by wrapping it in a setTimeout like this :
<form onChange={() => setTimeout(handleSubmit, 0)}>

React-final-form how is submit handled in the render of the form

Seems I can't get a grip on how React-final-form works.
There is a functional component with the following sections:
At the top:
import {Form, Field} from "react-final-form";
Then in the return:
return (
<Fragment>
<Form
onSubmit={handleSubmit}
render={ ({innerSubmit, form, submitting, pristine, values}) => (
<form onSubmit={innerSubmit}>
( Bunch of Fields... )
<div className="buttons">
<button type="submit" disabled={submitting}>
Save
</button>
<button
type="button"
onClick={form.reset}
disabled={submitting || pristine}>
Reset
</button>
</div>
</form>
)}
/>
</Fragment>
)
So, the render is a function which passes an object. In that object, the first one is 'innerSubmit'.
How is innerSubmit connected to the 'handleSubmit'?
And what are these values in the object (innerSubmit, form, submitting, pristine and values). The render function in React.dom (where render is coming from) has different parameters.
Please check documentation carefully. It seems that handleSumbit is kind of author's naming convention and should be used as a prop in render. On the other hand yours 'innerSumbit' (I suppose it's a form-handling function?) should be passed to 'onSumbit' in Form.
Probably a good idea for you is to check react-final-form author's yt tutorial.

Resources