How to add a Clear Button on a Formik Textfield - reactjs

I want to add a Clear Button as a convenience to users:
constructor(props) {
this.emailInput = React.createRef();
}
<Field label="Email" name="email" ref={this.emailInput} onChange={onChange} component={TextField}/>
But get this:
Warning: Function components cannot be given refs. Attempts to access this ref will fail.

To reset particular fields, use setFieldValue to set value to an empty string.
setFieldValue: (field: string, value: any, shouldValidate?: boolean) => void
Set the value of a field imperatively. field should match the key of values you wish to update. Useful for creating custom input change handlers.
- Formik Documentation
Eg:
<Formik
initialValues={initialValues}
...
>
{({ setFieldValue }) =>
...
<button type="reset" onClick={() => setFieldValue('fieldName', '')}>
Reset This
</button>
...
To reset all fields, use resetForm.
resetForm: (nextValues?: Values) => void
Imperatively reset the form. This will clear errors and touched, set isSubmitting to false, isValidating to false, and rerun mapPropsToValues with the current WrappedComponent's props or what's passed as an argument.
- Formik Documentation
Eg:
<Formik
initialValues={initialValues}
...
>
{({ resetForm }) =>
...
<button type="reset" onClick={resetForm}>
Reset All
</button>
...
Codesandbox : https://codesandbox.io/s/7122xmovnq

Formik has a built in method called resetForm which can be accessed like the other formik methods. In your form you probably have something like
<Formik
initialValues={something}
validationSchem={someSchema}
render={() => {
...some form stuff
}
}
/>
you can access the formik props inside that render method and do what you want with them:
<Formik
initialValues={something}
validationSchem={someSchema}
render={(props) => {
...some form stuff
<button type="button" onClick={() => props.resetForm()}>reset form</button>
}
}
/>

you can try to set a reset button in your form, e.g
<form>
<Field label="Email" name="email" onChange={onChange} component={TextField}/>
<input type="reset" value="Reset">
</form>
I took the example here, it has to reset all of the inputs in the form

Related

In React with Formik how can I build a search bar that will detect input value to render the buttons?

New to Formik and React I've built a search component that I'm having issues with the passing of the input value and rendering the buttons based on input length.
Given the component:
const SearchForm = ({ index, store }) => {
const [input, setInput] = useState('')
const [disable, setDisable] = useState(true)
const [query, setQuery] = useState(null)
const results = useLunr(query, index, store)
const renderResult = results.length > 0 || query !== null ? true : false
useEffect(() => {
if (input.length >= 3) setDisable(false)
console.log('input detected', input)
}, [input])
const onReset = e => {
setInput('')
setDisable(true)
}
return (
<>
<Formik
initialValues={{ query: '' }}
onSubmit={(values, { setSubmitting }) => {
setInput('')
setDisable(true)
setQuery(values.query)
setSubmitting(false)
}}
>
<Form className="mb-5">
<div className="form-group has-feedback has-clear">
<Field
className="form-control"
name="query"
placeholder="Search . . . . ."
onChange={e => setInput(e.currentTarget.value)}
value={input}
/>
</div>
<div className="row">
<div className="col-12">
<div className="text-right">
<button type="submit" className="btn btn-primary mr-1" disabled={disable}>
Submit
</button>
<button
type="reset"
className="btn btn-primary"
value="Reset"
disabled={disable}
onClick={onReset}
>
<IoClose />
</button>
</div>
</div>
</div>
</Form>
</Formik>
{renderResult && <SearchResults query={query} posts={results} />}
</>
)
}
I've isolated where my issue is but having difficulty trying to resolve:
<Field
className="form-control"
name="query"
placeholder="Search . . . . ."
onChange={e => setInput(e.currentTarget.value)}
value={input}
/>
From within the Field's onChange and value are my problem. If I have everything as posted on submit the passed query doesn't exist. If I remove both and hard code a true for the submit button my query works.
Research
Custom change handlers with inputs inside Formik
Issue with values Formik
Why is OnChange not working when used in Formik?
In Formik how can I build a search bar that will detect input value to render the buttons?
You need to tap into the props that are available as part of the Formik component. Their docs show a simple example that is similar to what you'll need:
<Formik
initialValues={{ query: '' }}
onSubmit={(values, { setSubmitting }) => {
setInput('')
otherStuff()
}}
>
{formikProps => (
<Form className="mb-5">
<div className="form-group has-feedback has-clear">
<Field
name="query"
onChange={formikProps.handleChange}
value={formikProps.values.query}
/>
</div>
<button
type="submit"
disabled={!formikProps.values.query}
>
Submit
</button>
<button
type="reset"
disabled={!formikProps.values.query}
onClick={formikProps.resetForm}
>
</Form>
{/* ... more stuff ... */}
)}
</Formik>
You use this render props pattern to pull formiks props out (I usually call them formikProps, but you can call them anything you want), which then has access to everything you need. Rather than having your input, setInput, disable, and setDisable variables, you can just reference what is in your formikProps. For example, if you want to disable the submit button, you can just say disable={!formikProps.values.query}, meaning if the query value in the form is an empty string, you can't submit the form.
As far as onChange, as long as you give a field the correct name as it corresponds to the property in your initialValues object, formikProps.handleChange will know how to properly update that value for you. Use formikProps.values.whatever for the value of the field, an your component will read those updates automatically. The combo of name, value, and onChange, all handled through formikProps, makes form handing easy.
Formik has tons of very useful prebuilt functionality to handle this for you. I recommend hanging out on their docs site and you'll see how little of your own code you have to write to handle these common form behaviors.

Formik validate initial values on page load

Code below validate when submit and onFocusOut of textbox fine, What I expect it to trigger validation first reload of the page with initial values.
Tried validateOnMount, as well as others but not worked.
Whats missing here?
const RoleValidationSchema = Yup.object().shape({
Adi: Yup.string()
.min(2, "En az 2 karakter olmalıdır")
.max(30, "En fazla 30 karakter olmalıdır")
.required("Gerekli!")
})
const Role = (props) => {
return (
<div>
<Formik
onSubmit={(values, { validate }) => {
debugger
validate(values);
alert("Submietted!")
props.handleFormSubmit()
}}
initialValues={{
Adi: "d"
}}
validationSchema={RoleValidationSchema}
validateOnMount={true}
validateOnChange={true}
validateOnBlur={true}
render={({ errors, touched, setFieldValue, ...rest }) => {
debugger
return (
<Form>
<Row>
<Col sm="12">
<Label for="Adi">Rol Adi</Label>
<FormGroup className="position-relative">
<Field
autoComplete="off"
id="Adi"
className="form-control"
name="Adi"
type="text"
/>
<ErrorMessage name="Adi">
{(msg) => (
<div className="field-error text-danger">{msg}</div>
)}
</ErrorMessage>
</FormGroup>
</Col>
<Tree dataSource={treeData} targetKeys={targetKeys} expandKeys={[]} onChange={onChange} />
</Row>
<button type="submit" className="btn btn-success" > Kaydet</button>
</Form>
)
}}
/>
</div>
)
}
This worked for me to show validation error on form load.
useEffect(() => {
if (formRef.current) {
formRef.current.validateForm()
}
}, [])
Try to add enableReinitialize Prop to your Formik Component
<Formik
enableReinitialize
......
/>
I had a bit specific case with dynamic Yup schema. Schema was generated based on API data. And validateOnMount was failing as when component was mounted there was no schema yet (API response not received yet).
I end up with something like this.
Inside component I used useEffect. Notice: I used useRef to be able to reference Formik outside of form.
useEffect(() => {
if (formRef.current) {
// check that schema was generated based on API response
if(Object.keys(dynamicValidationSchema).length != 0){
formRef.current.validateForm()
}
}
}, [initialData, dynamicValidationSchema])
Formik:
<Formik
innerRef={formRef}
enableReinitialize
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={props.handleSubmit}
>
It's possible to provide a initial value for validation in the "initialIsValid" prop.
const validationSchema= Yup.object()
const initialValues = { ... }
const initialIsValid = schema.isValidSync(initialValues)
return <Formik
initialValues={initialValues}
validationSchema={validationSchema}
initialIsValid={initialIsValid }
...
>
...
</Formik>
The only solution I found is to explicitly add initialErrors={{..}} to Formik.
In the below example, it's added depending on some condition. Unfortunately that duplicates the Yup validation/message which I also have in the schema. Nothing else worked, no validateOnMount or anything else.
<Formik
enableReinitialize
validationSchema={schema}
onSubmit={ (values) => {
submitForm(values);
}}
initialValues={initialFormValues}
/* Unfortunately a duplication of Yup Schema validation on this field,
but can't force Formik/Yup to validate initially */
initialErrors={!isEmptyObject(initialFormValues) &&
initialFormValues.inactiveApproverCondition &&
{'approverId': 'Selected Approver is no longer eligible. Please choose a different Approver to continue.'}}
>
You should try updating your code follow this
render={({ errors, touched, setFieldValue, isValid, ...rest })
and
<button type="submit" className="btn btn-success" disabled={!isValid}> Kaydet</button>

React final form triggers handleSubmit after the initial render

I've got only Switch component in my react-final-form. It looks like this:
<Form
onSubmit={onSubmit}
initialValues={initialValues}
render={({ handleSubmit }) => (
<form onSubmit={handleSubmit}>
<Field name="booleanValue" component={Switch} onChange={handleSubmit}/> //triggers when receives value
</form>
)
}
/>
I want to trigger handleSubmit only after user changes, not at first render of the form.
<Field/> doesn't have an onChange prop like you are attempting. Something like this could work.
import { OnChange } from 'react-final-form-listeners'
...
<Form
onSubmit={onSubmit}
initialValues={initialValues}
render={({ handleSubmit, form }) => (
<form onSubmit={handleSubmit}>
<Field name="booleanValue" component={Switch}/>
<OnChange name="booleanValue">
{(value, previousValue) => {
form.submit()
}}
</OnChange>
</form>
)
}
/>
P.S. I hope your Switch component knows to get its value and onChange from the input prop.
Hope that helps!

How to reset a certains fields after submit in Formik

I am using Formik and I need to make this logic.
I am have a big form, and after submission I need to reset certain fields in this form
There is 2 buttton: Save and Save & New
save will save the data and redirect.
Save & New will save the data and reset the first two fields.
I tried to use:
resetForm but it resets all the form
setValues does not work
setFieldValues prevents the api request
set values manually also does not work
Here is the code
const initialValues = { value1: "", value2: "", value3: "", value4: "" };
<Formik
initialValues={initialValues}
enableReinitialize
validationSchema={Schema}
validateOnChange={false}
validateOnBlur={false}
onSubmit={(values, { setSubmitting, resetForm }) => {
props.addData(values); // api request
setSubmitting(false);
// resetForm(initialValues); // this will reset all the form fields
// values.value1 = ""; // does not work
}}
>
{({ values, handleChange, handleSubmit, setFieldValue, errors }) => (
<Form>
<Row>
<Field type="text" name="value1" />
<Field type="text" name="value2" />
<Field type="text" name="value3" />
<Field type="text" name="value4" />
</Row>
<StyledRow>
<Button
variant="outline-primary"
onClick={() => {
handleSubmit();
history.push("/tableOverview");
}}
>
Save
</Button>
<Button
variant="outline-primary"
onClick={() => {
handleSubmit();
// this prevents the api request, because of async code I guess
// setFieldValue("value1", "");
// setFieldValue("value2", "");
}
>
Save & New
</Button>
</StyledRow>
</Form>
)}
</Formik>
On submitting the form, you can set whatever the values you dont want to peresist using setFieldValue and later setSubmitting to false, So that the values you want will be persisted and you can submit the form again
Please find the example codesandbox here

Clear a field's value on input clear in react-final-form

I'm using a custom component with react-final-form. On input change it sets the value to the address field. But when the input is cleared it doesn't update the value of the field. So I'm trying to do it with form mutators.
I have already added a mutator for clearing the field:
mutators={{
clear: ([address], state, { changeValue }) => {
changeValue(state, "address", () => undefined);
}
}}
I tried to add it to my custom onChange function, but it doesn't work.
onChange={event =>
props.input.onChange !== undefined
? props.input.onChange({ value: event })
: form.mutators.clear
}
Or maybe this can be done without mutators at all? I would really appreciate your help. Here is a live example (clearing the field works only on the button click as onClick={form.mutators.clear}).
You can just call form.change('address', undefined) at any time that you'd like to clear the value.
All the default callback are handle by the component. If you want to do a clear with a button click, you can create a custom component and use native callback methods do your thing.
onChange = (event) => {
this.setState({
address:event.target.value
});
}
onClear = () => {
this.setState({
address:''
});
}
<div>
<Field name="address">
<div>
<input
value={this.state.address}
onChange={this.onChange}
/>
</div>
</Field>
<button onClick={this.onClear}>Clear</button>
</div>
The problem is not with the react-final-form in your code, it is due to the react-da data, I have played a lot around your code within 1 day, and checked reset is working with fine with the react-final-form
Just update your render with this code and see reset is working absolutely fine. Yeah! the problem is with react da data. I am not sure about what it does due to less official information for this package.
<div className="App">
<h2>Dadata test</h2>
<Form
mutators={{
clear: ([address], state, { changeValue }) => {
changeValue(state, "address", () => undefined);
}
}}
onSubmit={onSubmit}
render={({ form, handleSubmit, pristine, invalid, values, errors }) => (
<form onSubmit={handleSubmit} noValidate>
<Field name="address" component="input" />
{/* {props => (
<div>
<ReactDadata
token="d19c6d0b94e64b21d8168f9659f64f7b8c1acd1f"
onChange={event =>
props.input.onChange !== undefined
? props.input.onChange({ value: event })
: form.mutators.clear
}
/>
</div>
)}
</Field> */}
<button type="submit" disabled={invalid}>
Submit
</button>
<button type="button" onClick={form.reset}>
Clear
</button>
<Fields names={["address"]}>
{fieldsState => (
<pre>{JSON.stringify(fieldsState, undefined, 2)}</pre>
)}
</Fields>
</form>
)}
/>
</div>
Hope it will help you to resolve the problem.

Resources