React,Formik和实现可重复表单字段(作为具有多个字段的表单)

时间:2019-10-16 23:16:18

标签: javascript reactjs formik

我正在尝试弄清如何与formik一起使用react将可重复的表单字段集成到我的表单中。 formik文档提供了how Field Arrays work的概述。我已经读了一百万遍,还不够聪明,无法理解。

我还阅读了此medium post,其中包括一个示例,在尝试学习如何制作可重复的表单元素时,我也尝试遵循该示例。

我之前曾问过这个问题,并尝试使用其中的建议,但是遇到了this stackblitz中显示的错误。我不知道如何解决它们。

不一致的人提供了此code sandbox example,以说明可重复字段应如何工作。我正在努力适应该示例以在其上下文中使用我的表单。在尝试嵌入具有多个表单字段的单独组件而不是一个名为title的表单元素之前,我根本无法使用remove元素。

我正在尝试:

  1. 具有一个带有一个按钮的父表单,该按钮显示添加数据请求;

  2. 单击该按钮时,显示另一个表单(有多个问题,当前设置为单独的类组件),询问有关数据请求的问题

  3. 具有一个显示“删除此数据请求”的按钮,以便可以删除完整的表单元素

  4. 重复上述步骤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>
          )}
        />

我们将很高兴收到任何有关解决此问题的建议。

1 个答案:

答案 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