How to use <Form.List /> (or something similar) in 'formik-antd'? - reactjs

I got simple question - Is it possible to use <Form.List /> in formik-antd? Tryed to find info about it but can't find anything in 3 hours. I need add dynamic fields in form, want to use <Form.List /> like in antd, but i use 'formik-antd'. looks like in 'formik-antd' no Form.List.. :( only Form.Item.
Any one know is the 'formik-antd' got <Form.List /> or something similar?

formik-antd is only used to make the components of antd compatible with formik, but something like Form.List isn't something that will be in formik-antd, but it comes from formik.
What you should use is FieldArray.
Example from docs:
Working codesandbox.
import React from 'react';
import { Formik, Form, Field, FieldArray } from 'formik';
// Here is an example of a form with an editable list.
// Next to each input are buttons for insert and remove.
// If the list is empty, there is a button to add an item.
export const FriendList = () => (
<div>
<h1>Friend List</h1>
<Formik
initialValues={{ friends: ['jared', 'ian', 'brent'] }}
onSubmit={values =>
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
}, 500)
}
render={({ values }) => (
<Form>
<FieldArray
name="friends"
render={arrayHelpers => (
<div>
{values.friends && values.friends.length > 0 ? (
values.friends.map((friend, index) => (
<div key={index}>
<Field name={`friends.${index}`} />
<button
type="button"
onClick={() => arrayHelpers.remove(index)} // remove a friend from the list
>
-
</button>
<button
type="button"
onClick={() => arrayHelpers.insert(index, '')} // insert an empty string at a position
>
+
</button>
</div>
))
) : (
<button type="button" onClick={() => arrayHelpers.push('')}>
{/* show this when user has removed all friends from the list */}
Add a friend
</button>
)}
<div>
<button type="submit">Submit</button>
</div>
</div>
)}
/>
</Form>
)}
/>
</div>
);

Just ran into the same Issue. My solution was to simply change the name in the import statement, just like so:
import {Form as antdForm} from 'antd';

Related

Add new form item to antd dynamic form list outside of the form

Based on this code snippet, I would like to reach and trigger the add property outside of the form. Any idea how can I do it?
<Form form={form} initialValues={initialValues} onFinish={onSubmit}>
<Form.List>
{(fields, { add, remove }) => (
<div>
{fields.map(field => (
<Form.Item {...field}>
<Input />
</Form.Item>
))}
</div>
)}
</Form.List>
// I don't want use this button here, inside the form list.
// I want invoke the add function from outside.
<Button type="link" onClick={() => add()}>
Add
</Button>
</Form>
// Or maybe from here
<Button type="link" onClick={() => add()}>
Add
</Button>

Using Form Inside Antd Modal

ERRORCODE:
index.js:1 Warning: Instance created by `useForm` is not connected to any Form element. Forget to pass `form` prop?
I am using React with Antd and I open a modal on a button click, the button passes some data which I am capturing via "e" and render them onto the modal.
The problem is whenever the modal is closed and opened again, the contents inside do not re render, it uses the previous event data. I've figured it is because I am not using the Form properly.
I will post the code below please let me know where I have gone wrong.
import react, antd ...etc
function App () => {
const sendeventdata(e) => {
const EventContent = () => (
<div>
<Form
form={form}
labelAlign="left"
layout="vertical"
initialValues={{
datePicker: moment(event.start),
timeRangePicker: [moment(event.start), moment(event.end)],
}}
>
<Form.Item name="datePicker" label={l.availability.date}>
<DatePicker className="w-100" format={preferredDateFormat} onChange={setValueDate} />
</Form.Item>
<Form.Item name="timeRangePicker" label={l.availability.StartToEnd}>
<TimePicker.RangePicker className="w-100" format="HH:mm" />
</Form.Item>
<span className="d-flex justify-content-end">
<Button
className="ml-1 mr-1"
onClick={() => {
form
.validateFields()
.then((values) => {
onUpdate(values)
form.resetFields()
})
.catch((info) => {
console.log('Validate Failed:', info)
})
}}
>
{l.availability.updateBtn}
</Button>
<Button danger className="ml-1 mr-1" onClick={() => handleDelete()}>
Delete
</Button>
</span>
</Form>
</div>
)
}
const MyModal = () => {
const { title, visible, content } = e
return (
<Modal
onCancel={handleModalClose}
title={title}
visible={visible}
footer={null}
form={form}
destroyOnClose
>
{content}
</Modal>
)
}
return <div><MyModal /><Button onClick{sendeventdata}></Button></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.

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

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.

Add multiple form fields but show only one at a time

I need final-form to be able to add array of form field records. But want to display only one field of array at a time. Like on left side I will have customer id or index which user will select and on right side i have to show customer corresponding to that index. I am able to add reac-final-form-array, but it always show all array elements. What should be the right approach to show only selected customer.
Please check the below code for reference. Hope my question is clear, if not please let me know, will add more explanation.
<FieldArray name="customer">
{({ fields }) => (
fields.map((name, index) => (
<div key={index}>
<Field name={`${name}.firstName`} />
<Field name={`${name}.lastName`} />
</div>
))
)}
</FieldArray>
To add new customer:
<div className="buttons">
<button
type="button"
onClick={() => push('customers', undefined)}>
Add Customer
</button>
</div>
Currently its looking like:
I need it to look like this:
In the fields array, you can add one more key isVisible.
It will look like this:
fields = [
{
firstName: 'John',
lastName: 'Doe',
isVisible: true,
},
{
firstName: 'Jane',
lastName: 'Doe',
isVisible: false,
}
];
Now while showing, only render the field when isVisible is true,
<FieldArray name="customer">
{({ fields }) => (
fields.map((name, index) => {
if(name.isVisible){
return (
<div key={index}>
<Field name={`${name}.firstName`} />
<Field name={`${name}.lastName`} />
</div>
);
))
)}
</FieldArray>
You can control isVisible key by clicking Cust # button.
Find the below code.
import React from "react";
import { Form, Field } from "react-final-form";
import arrayMutators from "final-form-arrays";
import { FieldArray } from "react-final-form-arrays";
const onSubmit = () => {
console.log("submitted");
};
const validate = () => {
// console.log("validated");
};
const MyForm = () => (
<Form
onSubmit={onSubmit}
mutators={{
// potentially other mutators could be merged here
...arrayMutators
}}
validate={validate}
render={({ handleSubmit, pristine, invalid }) => (
<form onSubmit={handleSubmit}>
<FieldArray name="customers">
{({ fields }) => (
<div>
<button
type="button"
onClick={() =>
fields.push({ firstName: "", lastName: "", isVisible: true })
}
>
Add Customer
</button>
{fields.map((name, index) => (
<div key={name}>
<a
onClick={() =>
(fields.value[index].isVisible = !fields.value[index]
.isVisible)
}
>{`Cust #${index}`}</a>
{fields.value[index].isVisible ? (
<div>
<div>
<Field name={`${name}.firstName`} component="input" />
</div>
<div>
<Field name={`${name}.lastName`} component="input" />
</div>
</div>
) : null}
<button type="button" onClick={() => fields.remove(index)}>
Remove
</button>
</div>
))}
</div>
)}
</FieldArray>
</form>
)}
/>
);
export default MyForm;
Check the codesandbox link here

Resources