我正在尝试弄清如何与formik一起使用react将可重复的表单字段集成到我的表单中。 formik文档提供了how Field Arrays work的概述。我已经读了一百万遍,还不够聪明,无法理解。
我还阅读了此medium post,其中包括一个示例,在尝试学习如何制作可重复的表单元素时,我也尝试遵循该示例。
我之前曾问过这个问题,并尝试使用其中的建议,但是遇到了this stackblitz中显示的错误。我不知道如何解决它们。
不一致的人提供了此code sandbox example,以说明可重复字段应如何工作。我正在努力适应该示例以在其上下文中使用我的表单。在尝试嵌入具有多个表单字段的单独组件而不是一个名为title的表单元素之前,我根本无法使用remove元素。
我正在尝试:
具有一个带有一个按钮的父表单,该按钮显示添加数据请求;
单击该按钮时,显示另一个表单(有多个问题,当前设置为单独的类组件),询问有关数据请求的问题
具有一个显示“删除此数据请求”的按钮,以便可以删除完整的表单元素
重复上述步骤1-3,以便可以有多个数据请求。
最接近工作的是上面的代码沙箱,但是我正在努力用具有多个表单字段的DataRequests.jsx表单组件替换单个标题表单元素。
任何人都可以看到该怎么做吗?
要在本文中列出代码,我的主要形式为:
import React from "react";
import ReactDOM from "react-dom";
import DataRequests from "./DataRequests";
// import { fsDB, firebase, settings } from "../../../firebase";
import { Formik, Form, Field, FieldArray, ErrorMessage, withFormik } from "formik";
// import * as Yup from "yup";
import Select from "react-select";
import {
Badge,
Button,
Col,
ComponentClass,
Feedback,
FormControl,
FormGroup,
FormLabel,
InputGroup,
Table,
Row,
Container
} from "react-bootstrap";
const style2 = {
paddingTop: "2em"
}
const initialValues = {
title: "",
DataRequests: [],
FundingRequests: [],
createdAt: ''
}
class ProjectForm extends React.Component {
state = {
options: [],
}
async componentDidMount() {
// const fsDB = firebase.firestore(); // Don't worry about this line if it comes from your config.
let options = [];
// await fsDB.collection("abs_for_codes").get().then(function (querySnapshot) {
// querySnapshot.forEach(function(doc) {
// console.log(doc.id, ' => ', doc.data());
// options.push({
// value: doc.data().title.replace(/( )/g, ''),
// label: doc.data().title + ' - ABS ' + doc.id
// });
// });
// });
this.setState({
options
});
}
handleSubmit = (formState, { resetForm }) => {
// Now, you're getting form state here!
const payload = {
...formState,
// resourceOffers: formState.resourceOffers.map(t => t.value),
// ethicsIssue: formState.ethicsIssue.map(t => t.value),
// disclosureStatus: formState.disclosureStatus.value,
createdAt: firebase.firestore.FieldValue.serverTimestamp()
}
console.log("formvalues", payload);
fsDB
.collection("project")
.add(payload)
.then(docRef => {
console.log("docRef>>>", docRef);
resetForm(initialValues);
})
.catch(error => {
console.error("Error adding document: ", error);
});
};
render() {
const { options } = this.state;
return(
<Formik
initialValues={initialValues}
// validationSchema={Yup.object().shape({
// title: Yup.string().required("Give your proposal a title")
// })}
onSubmit={this.handleSubmit}
render={({
errors,
status,
touched,
setFieldValue,
setFieldTouched,
handleSubmit,
isSubmitting,
dirty,
values,
arrayHelpers
}) => {
return (
<div>
<div className="formbox">
<Form>
<Table responsive>
<thead>
<tr>
<th>#</th>
<th>Element</th>
<th>Insights</th>
</tr>
</thead>
</Table>
{/*General*/}
<h5 className="formheading">general</h5>
<Table responsive>
<tbody>
<tr>
<td>1</td>
<td>
<div className="form-group">
<label htmlFor="title">Title</label>
<Field
name="title"
type="text"
className={
"form-control" +
(errors.title && touched.title ? " is-invalid" : "")
}
/>
<ErrorMessage
name="title"
component="div"
className="invalid-feedback"
/>
</div>
</td>
<td className="forminsight">No insights</td>
</tr>
</tbody>
</Table>
{/*Resources s*/}
<h5 className="formheading">resources</h5>
<Table responsive>
<tbody>
</tbody>
</Table>
<h6 className="formheading">Repeatable data form</h6>
<Table responsive>
<tbody>
<tr>
<td>2
</td>
<td>
<label htmlFor="DataRequests">Add a Data Request</label>
<Table responsive>
<tbody>
<tr>
<td>12
</td>
<td><label htmlFor="DataRequests">Add a Funding Request</label>
{/* <FieldArray name="fundingRequests" component={FundingRequests} /> */}
<tr>
<td>
<div>
<FieldArray
name="dataRequests"
component={DataRequests}
render={arrayHelpers => (
<React.Fragment>
{values.repeatable.map((r, i) => (
<div>
<label for={`repeatable.${i}.title`}>Title for {i}</label>
<Field name={`repeatable.${i}.title`} />
<Button
variant="outline-primary"
size="sm"
onClick={() => arrayHelpers.remove({ title: "" })}
>
Remove this Request
</Button>
</div>
))}
<div>
<button
type="button"
onClick={() => arrayHelpers.push({ title: "" })}
>
Add repeatable thing
</button>
</div>
</React.Fragment>
)}
/>
</div>
</td>
</tr>
</td>
<td className="forminsight">No insights</td>
</tr>
</tbody>
</Table>
<div className="form-group">
<Button
variant="outline-primary"
type="submit"
id="ProjectId"
onClick={handleSubmit}
disabled={!dirty || isSubmitting}
>
Save
</Button>
</div>
</Form>
</div>
</div>
);
}}
/>
);
}
}
export default ProjectForm;
ReactDOM.render(<ProjectForm />, document.getElementById("root"));
在上面的代码中,我保留了代码沙箱示例中的单个表单项(用于标题),以显示我想做什么的要点-该行项应替换为请求表单中的表单组件,如下:
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: "",
disclosure: ""
};
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" }
];
class DataRequests extends React.Component {
render() {
// Get the parent form props. This is where you should set the form values.
const {form: parentForm, ...parentProps} = this.props;
return (
<Formik
initialValues={initialValues}
render={({ values, form, push, remove, index, setFieldTouched }) => {
return (
<div>
{/* if i uncomment this line, i get loads of type errors saying that values is not defined
{parentForm.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"
onChange={e => {
parentForm.setFieldValue(
`dataRequests.${index}.title`,
e.target.value
);
}}
></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"
onChange={e => {
parentForm.setFieldValue(
`dataRequests.${index}.description`,
e.target.value
);
}}
></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}.dataType`}
className={"react-select-container"}
classNamePrefix="react-select"
value={values.dataTypes}
onChange={({ value: selectedOption }) => {
console.log(selectedOption);
// Setting field value - name of the field and values chosen.
parentForm.setFieldValue(
`dataRequests.${index}.dataType`,
selectedOption
);
}}
onBlur={setFieldTouched}
options={dataTypes}
/>
</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"
onChange={e => {
parentForm.setFieldValue(
`dataRequests.${index}.source`,
e.target.value
);
}}
></Field>
</div>
</td>
</tr>
<tr>
<td>
<div className="form-group">
<label htmlFor="dataRequestsDisclosure">
Do you anticipate disclosing this data?
</label>
<Field
name={`dataRequests.${index}.disclosure`}
component="textarea"
rows="10"
placeholder="Describe uses which may involve disclosure"
className="form-control"
onChange={e => {
parentForm.setFieldValue(
`dataRequests.${index}.disclosure`,
e.target.value
);
}}
></Field>
</div>
</td>
</tr>
<tr>
<Button
variant="link"
size="sm"
onClick={() => parentProps.remove(index)}
>
Remove this Data Request
</Button>
</tr>
</tbody>
</Table>
</div>
{/* );}) } */}
<Button
variant="outline-primary"
size="sm"
onClick={() => parentProps.push(initialValues)}
>
Add Data Request
</Button>
</div>
);
}}
/>
);
}
}
export default DataRequests;
因此,正如您所看到的,这都是一个混乱的大混乱。这是自今年5月以来每天尝试解决的结果。我显然不是很聪明,但是我想不出下一步该去哪里尝试学习的想法。
我试图修改Formik代码,以在可重复的FieldArray中使用许多表单字段,如下所示。为此,我从map函数中删除了“可重复”语句,但这会导致我不知道如何调试的错误。
const initialValues = {
title: "",
type: "",
identifier: "",
proposedUse: "",
relatedRights: ""
};
<Formik
initialValues={initialValues}
render={({ values }) => (
<Form>
<FieldArray
render={arrayHelpers => (
<React.Fragment>
{values.map((r, i) => (
<div>
<label for={`repeatable.${i}.title`}>Title</label>
<Field name={`repeatable.${i}.title`} />
<label for={`repeatable.${i}.classification`}>Classification</label>
<Field name={`repeatable.${i}.classification`} />
<label for={`repeatable.${i}.identifier`}>Identifier</label>
<Field name={`repeatable.${i}.identifier`} />
<label for={`repeatable.${i}.proposedUse`}>Proposed Use</label>
<Field name={`repeatable.${i}.proposedUse`} />
<label for={`repeatable.${i}.relatedRights`}>Related Rights</label>
<Field name={`repeatable.${i}.relatedRights`} />
<div>
<button
type="button"
onClick={() => arrayHelpers.remove({ values })}
>
Remove repeatable thing
</button>
</div>
</div>
))}
<div>
<button
type="button"
onClick={() => arrayHelpers.push({ values })}
>
Add repeatable thing
</button>
</div>
</React.Fragment>
)}
/>
</Form>
)}
/>
我们将很高兴收到任何有关解决此问题的建议。
答案 0 :(得分:0)
这是基本文件的外观。在此处将您的DataRequests组件导入到基本formik文件中。
import React from 'react'
import { Form, Formik, FieldArray } from 'formik'
import DataRequests from './components/DataRequests'
function App() {
return (
<Formik
onSubmit={values => console.log(values)}
initialValues={{ dataRequests: [] }}
render={({ values, setFieldValue }) => (
<Form>
<FieldArray
name='dataRequests'
render={arrayHelpers => (
<>
{values.dataRequests.map((data, i) => (
<DataRequests
setFieldValue={setFieldValue}
arrayHelpers={arrayHelpers}
values={values}
data={data}
key={i}
index={i}
/>
))}
<div>
<button
type='button'
onClick={() =>
arrayHelpers.push({
dataType: '',
title: '',
description: '',
source: '',
disclosure: ''
})
}
>
Add data request
</button>
</div>
</>
)}
/>
<button type='submit'>Submit</button>
</Form>
)}
/>
)
}
export default App
这就是您想要构造DataRequests组件的方式
import React from 'react'
import { Field } from 'formik'
import Select from 'react-select'
import { Table, Button } from 'react-bootstrap'
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' }
]
class DataRequests extends React.Component {
render() {
const { index, values, setFieldValue, arrayHelpers } = this.props
return (
<div>
<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='dataType'>
Are you looking for primary (raw) data or secondary data?
</label>
<Select
key={`my_unique_select_keydataType`}
name={`dataRequests.${index}.dataType`}
className={'react-select-container'}
classNamePrefix='react-select'
value={values.dataTypes}
onChange={({ value: selectedOption }) => {
console.log(selectedOption)
setFieldValue(
`dataRequests.${index}.dataType`,
selectedOption
)
}}
options={dataTypes}
/>
</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='dataRequestsDisclosure'>
Do you anticipate disclosing this data?
</label>
<Field
name={`dataRequests.${index}.disclosure`}
component='textarea'
rows='10'
placeholder='Describe uses which may involve disclosure'
className='form-control'
></Field>
</div>
</td>
</tr>
</tbody>
</Table>
<Button
variant='outline-primary'
size='sm'
onClick={() => arrayHelpers.remove(index)}
>
Remove this data request
</Button>
</div>
)
}
}
export default DataRequests
创建了一个代码框here