Input loses focus when using custom input for Formik Field in FieldArray - reactjs

I am using custom input components to handle my form data, and noticed strange behaviour. Only when I am using FieldArray with custom input components the input loses focus as I type.
Here is the form setup:
<Formik
enableReinitialize
initialValues={{ ...getInitialState() }}
onSubmit={(values, actions) => {
save(values, actions)
}}
validationSchema={schema}
>
{({ values, isSubmitting, setFieldValue, dirty }) => (
...
<Images />
...
)}
</Formik>
And inside my Images component
import { imageState } from "states"
import Input from "form/input"
function Images() {
...
return (
<div className={styles.wrapper}>
<FieldArray
name="images.posters"
render={({ form, push, remove }) => {
const images = form.values.images.poster
return (
<>
<button onClick={() => push(imageState)}>Add Poster</button>
{images.map((image, index) => (
<div key={index} className={styles.imageGroup}>
<Field //using custom input loses focus
name={`images.poster.${index}.src`}
component={Input}
/>
<Field //using Formik default component doesn't loose focus
name={`images.poster.${index}.src`}
/>
<button onClick={() => remove(index)}>Remove Poster</button>
</div>
))}
</>
)}}
/>
</div>
)
}
Is there another way I have to use custom inputs for FieldArray? I do not experience this problem when I am not using FieldArray.

Related

react-hook -form with SliderComponent input

Im new to react and I am trying to use a SliderComponent in a react Hook form but I cant seem able to fully understand how Controller works.
Here is my SliderComponent using react-input-slider:
export default function SliderComponent(props) {
const { axis, xmax, xmin, xstep, onChange } = props;;
return (
<div>
<Slider
axis={axis}
x={value.x}
xmax={xmax}
xmin={xmin}
xstep={xstep}
onChange={onChange}
/>
</div>
);
}
And here is my form:
export default function BiometricForm() {
const { handleSubmit, control } = useForm();
return (
<div className="registerForm_Container">
<form onSubmit={handleSubmit(onsubmit)}>
<Controller
control={control}
name="test"
render={({ props: { x, axis, xmax, xmin, xstep } }) => (
<SliderComponent
axis={"x"}
xmax={100}
xmin={1}
xstep={1}
value={x}
onChange={(e) => x.onChange(parseInt(e.target.value))}
/>
)}
/>
<button className="registerForm_Container_button" type="submit">
Register
</button>
</form>
</div>
);
}
I think it might be something to do with useState and that I am not able to reach useState of component. I have read that maybe its not necessary , any help? Thank you!
You are absolutely right, if you use RHF you don't need useState because RHF handles the form state for you. The important thing here is to pass an onChange handler to your <SliderComponent />, so that RHF can watch for value changes of your <SliderComponent /> and update the form state. You should also provide a defaultValue for the field, if you don't want it to be undefined if the user doesn't change the slider before submitting.
Also without the useState inside <SliderComponent />, you can also omit the <SliderComponent /> and just use <Slider />.
function BiometricForm() {
const { handleSubmit, control } = useForm();
const onSubmit = (data) => console.log(data);
return (
<div className="registerForm_Container">
<form onSubmit={handleSubmit(onSubmit)}>
<Controller
control={control}
name="test"
defaultValue={50}
render={({ field: { value, onChange } }) => (
<Slider
axis={"x"}
xmax={100}
xmin={1}
xstep={1}
onChange={({ x }) => onChange(x)}
x={value}
/>
)}
/>
<input type="submit" />
</form>
</div>
);
}

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.

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.

Handle submission from outside the form when using Formik

I have an ant design modal in which i have a Formik component. How can I handle form submission from outside the form ? I want to submit the form from the modal save button.
When the Formik component is used, the render method receives a set of props. One of them is called submitForm, you can call it to trigger a form submission outside of the form.
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);
}}
render={props => (
<div>
<form>
<input
type="text"
onChange={props.handleChange}
onBlur={props.handleBlur}
value={props.values.name}
name="name"
/>
</form>
<button onClick={props.submitForm}>Submit from outside the form</button>
</div>
)}
/>
</div>
);

Control the Redux-form with the button

I am using the Redux-form to do a task.
This form in a form container.
In the form container or in the form component.
There are two buttons. An add button and a subtract button.
The form component is:
import React from 'react'
import { Field, reduxForm } from 'redux-form'
import TextField from 'material-ui/TextField'
import RaisedButton from 'material-ui/RaisedButton'
const renderTextField = ({ input, label, meta: { touched, error }, ...custom }) => (
<TextField hintText={label}
floatingLabelText={label}
errorText={touched && error}
{...input}
{...custom}
/>
)
const ActivityDetailForm = props => {
const { handleSubmit, pristine, reset, submitting,} = props
return (
<form onSubmit={handleSubmit}>
<div>
<RaisedButton
type="submit"
disabled={pristine || submitting}
label="saveChange"
fullWidth={true}
secondary={true}
/>
</div>
</form>
)
}
export default reduxForm({
form: 'ActivityDetailForm', // a unique identifier for this form
})(ActivityDetailForm)
Now, I face a problem. When I click the add button,
<div>
<Field name="field1" component={renderTextField} label="text1: "/>
</div>
the code above will be created in the form element.
When I click the add button again, the div element which includes the Field named field2 will be created in the form element.
... Field named field3
... Field named field4
... Field named field5
...
When I click the subtract button. The last Field element will be destroyed.
Do you know the method to solve this problem?
The following (untested) is a pretty basic example on how to achieve dynamic inputs with a FieldArray. You'd have to tweak this a bit to tailor it to your specific scenario.
const renderTextField = ({ input, label, meta: { touched, error }, ...custom }) => (
<TextField hintText={label}
floatingLabelText={label}
errorText={touched && error}
{...input}
{...custom}
/>
)
const ActivityDetailForm = props => {
const { handleSubmit, pristine, reset, submitting,} = props
const renderFieldArray = ({ fields }) => (
<div>
<div>
<RaisedButton
onTouchTap={() => fields.push({})}
label="Add"
/>
</div>
{fields.map((field, index) => (
<div>
<div key={index}>
<Field
name={`${field}.name`}
label={`Text ${index + 1}`}
component={renderTextField}
/>
</div>
<div>
<RaisedButton
onTouchTap={() => fields.remove(index)}
label="Remove"
/>
</div>
</div>
))}
</div>
);
return (
<form onSubmit={handleSubmit}>
<div>
<FieldArray
name="textFields"
component={renderFieldArray}
/>
<RaisedButton
type="submit"
disabled={pristine || submitting}
label="saveChange"
fullWidth={true}
secondary={true}
/>
</div>
</form>
)
}
export default reduxForm({
form: 'ActivityDetailForm', // a unique identifier for this form
})(ActivityDetailForm)

Resources