React - Formik - 字段数组 - 实现可重复的表单字段
React - Formik - Field Arrays - implementing repeatable form fields
我正在尝试按照 Formik 文档使用 FieldArrays 以便我可以将可重复的表单元素添加到我的表单中。
我也看过这个 Medium post 设置示例。
我学起来很慢,无法在文档和实现之间加入点。
我想在我的主窗体中有一个按钮,上面写着:"Add a request for data"。
如果该按钮是 selected,则会显示设置数据配置文件的嵌套表单,以及 "add another data request" 和 "remove" 按钮。
我已经在我的应用程序的另一个组件中制作了嵌套表单,但我正在努力弄清楚如何使用来自媒体 post 的示例来合并嵌套表单(作为可重复元素 -即有人可能需要 5 个数据请求)。
有没有关于如何实现这个的例子?
在我的代码中,我基本上遵循了中等 post,但尝试 link 索引
中的数据请求表单组件
<button
type="button"
onClick={() => arrayHelpers.insert(index, <DataRequestForm />)}>
Add a data request
</button>
这显然是不正确的,但我不知道该怎么做。
根据 Nithin 的回答,我尝试修改嵌入式表单以便我可以使用 react-select,如下所示,但我收到一条错误消息:
TypeError: Cannot read property 'values' of undefined
import React from "react";
import { Formik, Form, Field, FieldArray, ErrorMessage, withFormik } from "formik";
import Select from "react-select";
import {
Button,
Col,
FormControl,
FormGroup,
FormLabel,
InputGroup,
Table,
Row,
Container
} from "react-bootstrap";
const initialValues = {
dataType: "",
title: "",
description: '',
source: '',
}
class DataRequests extends React.Component {
render() {
const dataTypes = [
{ value: "primary", label: "Primary (raw) data sought" },
{ value: "secondary", label: "Secondary data sought"},
{ value: "either", label: "Either primary or secondary data sought"},
{ value: "both", label: "Both primary and secondary data sought"}
]
return(
<Formik
initialValues={initialValues}
render={({
form,
remove,
push,
errors,
status,
touched,
setFieldValue,
setFieldTouched,
handleSubmit,
isSubmitting,
dirty,
values
}) => {
return (
<div>
{form.values.dataRequests.map((_notneeded, index) => {
return (
<div key={index}>
<Table responsive>
<tbody>
<tr>
<td>
<div className="form-group">
<label htmlFor="dataRequestsTitle">Title</label>
<Field
name={`dataRequests.${index}.title`}
placeholder="Add a title"
className={"form-control"}
>
</Field>
</div>
</td>
</tr>
<tr>
<td>
<div className="form-group">
<label htmlFor="dataRequestsDescription">Description</label>
<Field
name={`dataRequests.${index}.description`}
component="textarea"
rows="10"
placeholder="Describe the data you're looking to use"
className={
"form-control"}
>
</Field>
</div>
</td>
</tr>
<tr>
<td>
<div className="form-group">
<label htmlFor="dataRequestsSource">Do you know who or what sort of entity may have this data?</label>
<Field
name={`dataRequests.${index}.source`}
component="textarea"
rows="10"
placeholder="Leave blank and skip ahead if you don't"
className={
"form-control"}
>
</Field>
</div>
</td>
</tr>
<tr>
<td>
<div className="form-group">
<label htmlFor="dataType">
Are you looking for primary (raw) data or secondary data?
</label>
<Select
key={`my_unique_select_keydataType`}
name={`dataRequests.${index}.source`}
className={
"react-select-container"
}
classNamePrefix="react-select"
value={values.dataTypes}
onChange={selectedOptions => {
// Setting field value - name of the field and values chosen.
setFieldValue("dataType", selectedOptions)}
}
onBlur={setFieldTouched}
options={dataTypes}
/>
</div>
</td>
</tr>
<tr>
<Button variant='outline-primary' size="sm" onClick={() => remove(index)}>
Remove
</Button>
</tr>
</tbody>
</Table>
</div>
);
})}
<Button
variant='primary' size="sm"
onClick={() => push({ requestField1: "", requestField2: "" })}
>
Add Data Request
</Button>
</div>
)
}
}
/>
);
};
};
export default DataRequests;
您不能在表单元素内添加嵌套表单。
模式详情请参考下方post
Can you nest html forms?
如果您希望在主窗体中使用嵌套结构嵌套多个字段,您可以使用 FieldArrays
。
您可以像这样构建表单。
{
firstName: "",
lastName: "",
dataRequests: []
}
此处 firstName
和 lastName
是顶级表单字段,dataRequests
可以是一个数组,其中每个元素都遵循结构
{
requestField1: "",
requestField2: ""
}
由于 dataRequests
是一个数组,要渲染 FieldArray
的每个项目,您需要一个映射函数。
form.values.dataRequests.map( render function() )
并且对于每个呈现的项目,更改处理程序应以其索引为目标以更新 FieldArray
中的正确项目。
<div key={index}>
<Field
name={`dataRequests.${index}.requestField1`}
placeholder="requestField1"
></Field>
<Field
name={`dataRequests.${index}.requestField2`}
placeholder="requestField2"
></Field>
<button type="button" onClick={() => remove(index)}>
Remove
</button>
</div>
在上面的代码片段中,name={dataRequests.${index}.requestField1}
要求 formik 使用输入字段的值更新 dataRequests
数组的 nth
元素的键 requestField1
。
最后,您的 <DataRequest />
组件可能如下所示。
import React from "react";
import { Field } from "formik";
export default ({ form, remove, push }) => {
return (
<div>
{form.values.dataRequests.map((_notneeded, index) => {
return (
<div key={index}>
<Field
name={`dataRequests.${index}.requestField1`}
placeholder="requestField1"
></Field>
<Field
name={`dataRequests.${index}.requestField2`}
placeholder="requestField2"
></Field>
<button type="button" onClick={() => remove(index)}>
Remove
</button>
</div>
);
})}
<button
type="button"
onClick={() => push({ requestField1: "", requestField2: "" })}
>
Add Data Request
</button>
</div>
);
};
并且使用 <FieldArray />
您可以将 <DataRequest />
连接到主窗体。
您可以尝试下面的示例 SO 片段
function DataRequests({ form, remove, push }){
return (
<div>
{form.values.dataRequests.map((_notneeded, index) => {
return (
<div key={index}>
<Formik.Field
name={`dataRequests.${index}.requestField1`}
placeholder="requestField1"
></Formik.Field>
<Formik.Field
name={`dataRequests.${index}.requestField2`}
placeholder="requestField2"
></Formik.Field>
<button type="button" onClick={() => remove(index)}>
Remove
</button>
</div>
);
})}
<button
type="button"
onClick={() => push({ requestField1: "", requestField2: "" })}
>
Add Data Request
</button>
</div>
);
};
class Home extends React.Component {
initialValues = {
firstName: "",
lastName: "",
dataRequests: []
};
state = {};
render() {
return (
<div>
<Formik.Formik
initialValues={this.initialValues}
onSubmit={values => {
this.setState({ formData: values });
}}
>
{() => {
return (
<Formik.Form>
<div>
<Formik.Field
name="firstName"
placeholder="First Name"
></Formik.Field>
</div>
<div>
<Formik.Field
name="lastName"
placeholder="Last Name"
></Formik.Field>
</div>
<Formik.FieldArray name="dataRequests" component={DataRequests} />
<button type="submit">Submit</button>
</Formik.Form>
);
}}
</Formik.Formik>
{this.state.formData ? (
<code>{JSON.stringify(this.state.formData, null, 4)}</code>
) : null}
</div>
);
}
}
ReactDOM.render(<Home />, document.getElementById("root"))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/formik/dist/formik.umd.production.js"></script>
<div id="root"></div>
对于任何想从中学习的人 post,nithin 对这个问题的回答显然是出于善意,但这不是 formik 的正确部署。您可以在此处查看代码沙箱:https://codesandbox.io/embed/goofy-glade-lx65p?fontsize=14 当前尝试解决此问题(仍未修复)但朝着解决方案迈出了更好的一步。同样感谢这个问题的答案背后的帮助意图。
我正在尝试按照 Formik 文档使用 FieldArrays 以便我可以将可重复的表单元素添加到我的表单中。
我也看过这个 Medium post 设置示例。
我学起来很慢,无法在文档和实现之间加入点。
我想在我的主窗体中有一个按钮,上面写着:"Add a request for data"。
如果该按钮是 selected,则会显示设置数据配置文件的嵌套表单,以及 "add another data request" 和 "remove" 按钮。
我已经在我的应用程序的另一个组件中制作了嵌套表单,但我正在努力弄清楚如何使用来自媒体 post 的示例来合并嵌套表单(作为可重复元素 -即有人可能需要 5 个数据请求)。
有没有关于如何实现这个的例子?
在我的代码中,我基本上遵循了中等 post,但尝试 link 索引
中的数据请求表单组件<button
type="button"
onClick={() => arrayHelpers.insert(index, <DataRequestForm />)}>
Add a data request
</button>
这显然是不正确的,但我不知道该怎么做。
根据 Nithin 的回答,我尝试修改嵌入式表单以便我可以使用 react-select,如下所示,但我收到一条错误消息:
TypeError: Cannot read property 'values' of undefined
import React from "react";
import { Formik, Form, Field, FieldArray, ErrorMessage, withFormik } from "formik";
import Select from "react-select";
import {
Button,
Col,
FormControl,
FormGroup,
FormLabel,
InputGroup,
Table,
Row,
Container
} from "react-bootstrap";
const initialValues = {
dataType: "",
title: "",
description: '',
source: '',
}
class DataRequests extends React.Component {
render() {
const dataTypes = [
{ value: "primary", label: "Primary (raw) data sought" },
{ value: "secondary", label: "Secondary data sought"},
{ value: "either", label: "Either primary or secondary data sought"},
{ value: "both", label: "Both primary and secondary data sought"}
]
return(
<Formik
initialValues={initialValues}
render={({
form,
remove,
push,
errors,
status,
touched,
setFieldValue,
setFieldTouched,
handleSubmit,
isSubmitting,
dirty,
values
}) => {
return (
<div>
{form.values.dataRequests.map((_notneeded, index) => {
return (
<div key={index}>
<Table responsive>
<tbody>
<tr>
<td>
<div className="form-group">
<label htmlFor="dataRequestsTitle">Title</label>
<Field
name={`dataRequests.${index}.title`}
placeholder="Add a title"
className={"form-control"}
>
</Field>
</div>
</td>
</tr>
<tr>
<td>
<div className="form-group">
<label htmlFor="dataRequestsDescription">Description</label>
<Field
name={`dataRequests.${index}.description`}
component="textarea"
rows="10"
placeholder="Describe the data you're looking to use"
className={
"form-control"}
>
</Field>
</div>
</td>
</tr>
<tr>
<td>
<div className="form-group">
<label htmlFor="dataRequestsSource">Do you know who or what sort of entity may have this data?</label>
<Field
name={`dataRequests.${index}.source`}
component="textarea"
rows="10"
placeholder="Leave blank and skip ahead if you don't"
className={
"form-control"}
>
</Field>
</div>
</td>
</tr>
<tr>
<td>
<div className="form-group">
<label htmlFor="dataType">
Are you looking for primary (raw) data or secondary data?
</label>
<Select
key={`my_unique_select_keydataType`}
name={`dataRequests.${index}.source`}
className={
"react-select-container"
}
classNamePrefix="react-select"
value={values.dataTypes}
onChange={selectedOptions => {
// Setting field value - name of the field and values chosen.
setFieldValue("dataType", selectedOptions)}
}
onBlur={setFieldTouched}
options={dataTypes}
/>
</div>
</td>
</tr>
<tr>
<Button variant='outline-primary' size="sm" onClick={() => remove(index)}>
Remove
</Button>
</tr>
</tbody>
</Table>
</div>
);
})}
<Button
variant='primary' size="sm"
onClick={() => push({ requestField1: "", requestField2: "" })}
>
Add Data Request
</Button>
</div>
)
}
}
/>
);
};
};
export default DataRequests;
您不能在表单元素内添加嵌套表单。 模式详情请参考下方post
Can you nest html forms?
如果您希望在主窗体中使用嵌套结构嵌套多个字段,您可以使用 FieldArrays
。
您可以像这样构建表单。
{
firstName: "",
lastName: "",
dataRequests: []
}
此处 firstName
和 lastName
是顶级表单字段,dataRequests
可以是一个数组,其中每个元素都遵循结构
{
requestField1: "",
requestField2: ""
}
由于 dataRequests
是一个数组,要渲染 FieldArray
的每个项目,您需要一个映射函数。
form.values.dataRequests.map( render function() )
并且对于每个呈现的项目,更改处理程序应以其索引为目标以更新 FieldArray
中的正确项目。
<div key={index}>
<Field
name={`dataRequests.${index}.requestField1`}
placeholder="requestField1"
></Field>
<Field
name={`dataRequests.${index}.requestField2`}
placeholder="requestField2"
></Field>
<button type="button" onClick={() => remove(index)}>
Remove
</button>
</div>
在上面的代码片段中,name={dataRequests.${index}.requestField1}
要求 formik 使用输入字段的值更新 dataRequests
数组的 nth
元素的键 requestField1
。
最后,您的 <DataRequest />
组件可能如下所示。
import React from "react";
import { Field } from "formik";
export default ({ form, remove, push }) => {
return (
<div>
{form.values.dataRequests.map((_notneeded, index) => {
return (
<div key={index}>
<Field
name={`dataRequests.${index}.requestField1`}
placeholder="requestField1"
></Field>
<Field
name={`dataRequests.${index}.requestField2`}
placeholder="requestField2"
></Field>
<button type="button" onClick={() => remove(index)}>
Remove
</button>
</div>
);
})}
<button
type="button"
onClick={() => push({ requestField1: "", requestField2: "" })}
>
Add Data Request
</button>
</div>
);
};
并且使用 <FieldArray />
您可以将 <DataRequest />
连接到主窗体。
您可以尝试下面的示例 SO 片段
function DataRequests({ form, remove, push }){
return (
<div>
{form.values.dataRequests.map((_notneeded, index) => {
return (
<div key={index}>
<Formik.Field
name={`dataRequests.${index}.requestField1`}
placeholder="requestField1"
></Formik.Field>
<Formik.Field
name={`dataRequests.${index}.requestField2`}
placeholder="requestField2"
></Formik.Field>
<button type="button" onClick={() => remove(index)}>
Remove
</button>
</div>
);
})}
<button
type="button"
onClick={() => push({ requestField1: "", requestField2: "" })}
>
Add Data Request
</button>
</div>
);
};
class Home extends React.Component {
initialValues = {
firstName: "",
lastName: "",
dataRequests: []
};
state = {};
render() {
return (
<div>
<Formik.Formik
initialValues={this.initialValues}
onSubmit={values => {
this.setState({ formData: values });
}}
>
{() => {
return (
<Formik.Form>
<div>
<Formik.Field
name="firstName"
placeholder="First Name"
></Formik.Field>
</div>
<div>
<Formik.Field
name="lastName"
placeholder="Last Name"
></Formik.Field>
</div>
<Formik.FieldArray name="dataRequests" component={DataRequests} />
<button type="submit">Submit</button>
</Formik.Form>
);
}}
</Formik.Formik>
{this.state.formData ? (
<code>{JSON.stringify(this.state.formData, null, 4)}</code>
) : null}
</div>
);
}
}
ReactDOM.render(<Home />, document.getElementById("root"))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/formik/dist/formik.umd.production.js"></script>
<div id="root"></div>
对于任何想从中学习的人 post,nithin 对这个问题的回答显然是出于善意,但这不是 formik 的正确部署。您可以在此处查看代码沙箱:https://codesandbox.io/embed/goofy-glade-lx65p?fontsize=14 当前尝试解决此问题(仍未修复)但朝着解决方案迈出了更好的一步。同样感谢这个问题的答案背后的帮助意图。