Clear Formik field with initial value React - javascript

CodeSandbox:
https://codesandbox.io/s/kind-fire-q4o45
Question:
Click on reset button and clear a field with value "initial value"
Attempts:
There are too many variants to reset form via:
resetForm()
setFieldValue(<your_field_name>, '')
form.current.reset()
But this list doesn't helpful when you have initial value in formik field.
Snippet:
import React from 'react'
import {Formik, Form, Field} from 'formik'
const Search = () => (
<Formik onSubmit={({q}, {setSubmitting}) => {
setSubmitting(false)
}} initialValues={{q: 'initial value'}} render={({resetForm}) => (
<Form>
<Field name='q' />
<button type="reset" onClick={() => resetForm()}>Reset</button> {/* <== Reset */}
</Form>
)}/>
)

You're totally right - if you have some initial form state, the resetForm action will set the values to those initials. setFieldValue probably the only way to manually clear the field:
<button type="button" onClick={() => setFieldValue('q', '')}>
Drop field
</button>
notice, type='reset' not need here...
In case when you need to drop multiple fields, take a look at this method:
setValues({q: ''})

You can now reset formik by
formik.resetForm({
values: { name: 'Custom initial values', email: '' },
});
https://formik.org/docs/migrating-v2#resetform

yes when you reset form values it will reset it to default value
you can do the following
<Formik
enableReinitialize
onSubmit={(values, { setSubmitting }) => {
values.q = '';
setSubmitting(false);
}}
initialValues={{ q: "initial value" }}
render={({ resetForm }) => (
<Form>
<Field name="q" />
<button type="submit">
Reset form
</button>{" "}
{/* <== Reset */}
</Form>
)}
/>
Hope it helps

Related

Formik - Show ErrorMessage on Blur

On the code below there is a text input that shows an error message when:
type invalid email (or leave blank) and hit enter
type invalid email (or leave blank) and blur (or clicking outside)
import { ErrorMessage, Field, Form, Formik } from 'formik';
import React from 'react';
import * as Yup from 'yup';
const TextInput = ({ value, handleChange }) => (
<input
type="text"
value={value}
onChange={(e) => handleChange(e.target.value)}
/>
);
export default () => {
return (
<Formik
initialValues={{
email: '',
}}
validationSchema={Yup.object().shape({
email: Yup.string()
.required('Email is required.')
.email('Email is invalid.'),
})}
onSubmit={(values, { setSubmitting }) => {
console.log(values);
setSubmitting(false);
}}
enableReinitialize
>
{({ setFieldValue }) => (
<Form>
<div>
<Field
type="text"
name="email"
onChange={({ target: { value } }) => {
console.log(value);
setFieldValue('email', value);
}}
/>
<ErrorMessage name="email">
{(error) => <div style={{ color: '#f00' }}>{error}</div>}
</ErrorMessage>
</div>
<input type="submit" value="Submit" />
</Form>
)}
</Formik>
);
};
Now I want to keep the same behavior when doing the following change:
from
<Field
type="text"
name="email"
onChange={({target:{value}}) => {
console.log(value);
setFieldValue("email", value);
}}
/>
to:
<Field
component={TextInput}
name="email"
handleChange={(value) => {
console.log(value);
setFieldValue("email", value);
}}
/>
I mean, basically I'm changing from: type="text" to component={TextInput} which is basically a text input as you can see above.
After doing that change the error happens when:
[SUCCESS] type invalid email (or leave blank) and hit enter
but not when:
[FAIL] type invalid email (or leave blank) and blur (or clicking outside)
Do you know how can I get the error displayed on the second situation?
I'm looking for a standard way to do it and not tricky workarounds.
Here you have a StackBlitz you can play with and if you want you can post your forked one.
Thanks!
The way I handle this is to pass name and id between the component and wrapper, otherwise, Formik has no relationship and therefore no knowledge of what to error.
Here's a fork
import { ErrorMessage, Field, Form, Formik } from 'formik';
import React from 'react';
import * as Yup from 'yup';
const TextInput = ({ value, handleChange, name, id }) => (
<Field
id={id} // <- adding id
name={name} // <- adding name
type="text"
value={value}
onChange={handleChange} // You don't need to capture the event, you've already done the work in the wrapper
/>
);
export default () => {
return (
<Formik
initialValues={{
email: '',
}}
validationSchema={Yup.object().shape({
email: Yup.string()
.required('Email is required.')
.email('Email is invalid.'),
})}
onSubmit={(values, { setSubmitting }) => {
console.log(values);
setSubmitting(false);
}}
enableReinitialize
>
{({ setFieldValue }) => (
<Form>
<div>
<Field
component={TextInput}
id="email" // <- id
name="email" // <- name
handleChange={e => {
const value = e.target.value;
console.log(value);
setFieldValue('email', value);
}}
/>
<ErrorMessage name="email">
{(error) => <div style={{ color: '#f00' }}>{error}</div>}
</ErrorMessage>
</div>
<input type="submit" value="Submit" />
</Form>
)}
</Formik>
);
};

Why dynamic values are not being submitted from form with NextJS?

I'm working on React based on NextJS code.
If I manually enter values in form fields and submit the form, I get all the values in the component function.
But if I dynamically display values in a form field like value={query.campaignid}, the component does not collect any value.
import { Button, Input } from "#chakra-ui/react";
import { Field, Form, Formik } from "formik";
import { useRouter } from "next/router";
export default function FormikExample() {
const { query } = useRouter();
return (
<>
<Formik
initialValues={
{
// campaignid: " ",
}
}
onSubmit={(values) => {
setTimeout(() => {
console.log(values);
});
}}
>
{(props) => (
<Form>
<Field name="campaignid">
<Input value={query.campaignid} />
</Field>
<Button id="submited" isLoading={props.isSubmitting} type="submit">
Submit
</Button>
</Form>
)}
</Formik>
</>
);
}
Resolved!
I passed the URL query to initialValues and added enableReinitialize={true} below:
...
initialValues={{
campaignId: query.campaignid,
}}
enableReinitialize={true}
...
In the input value I passed those props:
<Field name="campaignid">
<Input value={props.values.campaignId} />
</Field>

Updating one of the Formik initial values with state resets all other values

I currently have a form I am building using formik. I am able to update each of the fields(name, email, phone) as expected. The issue I am running into is when I provide one of the formik intial values with a state value and update that state. The act of updating the state causes the form to completely reset the input fields Is there a way to update the test state without resetting the entire form?? My code is as follows:
import { useState } from "react";
import { Formik, FieldArray } from "formik";
import "./styles.css";
export default function App() {
const [test, setTest] = useState(null);
return (
<Formik
enableReinitialize
initialValues={{
test: test,
name: "",
email: "",
phone: ""
}}
>
{({
values,
touched,
errors,
handleChange,
handleBlur,
handleSubmit
}) => (
<>
<button onClick={() => setTest("something")}>Update State</button>
<br />
<input
placeholder="name"
type="text"
name="name"
value={values.name}
onChange={handleChange}
/>
<br />
<input
placeholder="email"
type="text"
name="email"
value={values.email}
onChange={handleChange}
/>
<br />
<input
placeholder="phone"
type="text"
name="phone"
value={values.phone}
onChange={handleChange}
/>
<>
<pre style={{ textAlign: "left" }}>
<strong>Values</strong>
<br />
{JSON.stringify(values, null, 2)}
</pre>
<pre style={{ textAlign: "left" }}>
<strong>Errors</strong>
<br />
{JSON.stringify(errors, null, 2)}
</pre>
</>
</>
)}
</Formik>
);
}
In order to replicate what I am talking about in the codesandbox first update the name, email, and phone number. Then hit the udate state button and everything will be reset
codesandbox https://codesandbox.io/s/wispy-sun-mzeuy?file=/src/App.js:0-1567
This is because you are initializing the Formik component with enableReinitialize set to true:
enableReinitialize?: boolean
Default is false. Control whether Formik should reset the form if initialValues changes (using deep equality).
When the state variable test is updated, the formik-values are reinitialized to the values set in initialValues.
If you want this to work, I've forked your sandbox and modified it to use the formik hook (without enableReinitialize) in combination with a useEffect to manually update the value from state:
const [test, setTest] = useState(null);
const { values, errors, handleChange, setFieldValue } = useFormik({
initialValues: {
test: test,
name: "",
email: "",
phone: ""
}
});
useEffect(() => {
setFieldValue("test", test);
}, [test]);
My solution for this problem was to use the Formik values as state and set it directly with setFieldValue, so enableInitialize would not be triggered.
export default function App() {
//const [test, setTest] = useState(null); // This removed
return (
<Formik
enableReinitialize
initialValues={{
test: "",
name: "",
email: "",
phone: ""
}}
>
...
<button onClick={() => setFieldValue("test", "something")}>Update State</button>
Mostly the reason you would want to use states outside of Formik is to use it outside of Formik (ok clear), but this way you still can trigger a function from outside of Formik which you pass the values and the setFieldValue- Function to do whatever when triggered e.g. at onChange of a Formik field:
const handlerDefinedOutside = (values: any, setFieldValue: (field: string, value: any, shouldValidate?: boolean) => void): void => { /* do stuff */ };
<TextField
onChange={(e) => {
handleChange(e);
handlerDefinedOutside(values, setFieldValue);
}}
/>

Formik React with 2 buttons (Submit and Save) to submit form - Save button not to trigger validation

Pretty new with Formik, I have a simple form, which has validation. I need to have 2 buttons, Submit and a Save button which will mostly do the same thing, however, if "Save" button is clicked, I want the validation to be "disabled" or rather, all required fields will no longer be required. Any ideas how I can achieve this?
Some codes below:
const initialValues = {
title: "",
description: ""
};
const validationSchema = Yup.object().shape({
title: Yup.string()
.max(50, 'You may only enter up to 50 characters')
.required('Required'),
description: Yup.string()
.max(200, 'You may only enter up to 200 characters')
.required('Required'),
})
const CreateForm = () => {
const handleCancel = () => {
alert("Cancelled!")
}
return (
<div>
<Formik initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={(values) => {
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
}, 3000)
}}
>
{props => (
<Form>
<CustomTextInput label="Title"
name="title" type="input" placeholder="Enter Title" />
<CustomTextInput label="Description"
name="description" type="input" placeholder="Description" />
<div>
<Button type="submit" variant="contained" color="primary">
Submit
</Button>
<Button type="submit" variant="contained" color="secondary" >
Save
</Button>
<Button variant="contained" color="default" onClick={handleCancel}>
Cancel
</Button>
</div>
</Form>
)}
</Formik>
</div>
)
}
export default CreateForm
Firstly remove type="submit". Because formik will understand it like submit and will validate it. Second add onClick function:
<Button
variant="contained"
color="secondary"
onClick={() => handleSave(props.values)} >Save</Button>
And:
const handleSave = (values) => {
// do what you want like on submit
}
Create a variable
const [btnClicked, setBtnClicked] = React.useState('');
Add the onClick event to buttons setting a value
<form onSubmit={formik.handleSubmit}>
<Button type="submit" onClick={(event)=>{
setBtnClicked('save');
}}> Save </Button>
<Button type="submit" onClick={(event)=>{
setBtnClicked('save_and_close');
}}>Save and close </Button>
</form>
On the formik submit event you can get the value of the button selected to do whatever you want
const formik = useFormik({
initialValues: { },
validationSchema: yup.object({ }),
onSubmit: (values) => {
if(btnClicked == "save"){
// option 1
} else {
// Option 2
}
}
});

Can I access a value in Formik before onSubmit is run?

I am trying to do some maths over a value that the user inputs
<Form>
<label htmlFor={`price${props.id}`}>Price</label>
<Field
name={`price${props.id}`}
type="text"
/>
<ErrorMessage name={`price${props.id}`} />
<span> </span> //Where I want to display value
...
This is how I am using Formik. Form is simply nested inside of Formik:
<Formik
initialValues={{
[`price${props.id}`]: '',
}}
onSubmit={(values, { setSubmitting }) => {
setTimeout(() => {
alert(JSON.stringify(values, null, 2))
setSubmitting(false)
}, 400)
}}>
<Form>
....
</Form>
</Formik>
Formik accepts two types of children:
React node
Function that returns a React node
Currently you are passing a React node to Formik (#1).
In order to access values you need to change the child to a function (#2), which is known as a render prop.
Values can then be accessed like so:
<Formik
initialValues={/* ... */}
onSubmit={/* ... */}
>
{props => (
<form>
{/* here we can access props.values */}
</form>
)}
</Formik>
A full list of what props contain can be found here.
<Formik> can provide a child function that will allow you to access all of it's props, including current values -
<Formik>
{{ values } => (
<Form>
<label htmlFor={`price${props.id}`}>Price</label>
<Field
name={`price${props.id}`}
type="text"
/>
<ErrorMessage name={`price${props.id}`} />
<span>
// will show the current value, or you can do calculations
{values[`price${props.id}`]}
</span>
</Form>
)}
</Formik>

Categories

Resources