React - Formik - Field Arrays - реализация повторяющихся полей формы - PullRequest
1 голос
/ 13 октября 2019

Я пытаюсь следовать документации Formik по использованию FieldArrays , чтобы я мог добавить повторяющиеся элементы формы в мою форму.

Я также видел это Средняя запись изложение примера.

Я медленно учусь и не могу соединить точки между документацией и реализацией.

Я хочу, чтобы в моей основной форме была кнопка: «Добавить запрос наdata ".

Если эта кнопка выбрана, то отображается вложенная форма, задающая профиль данных, а также кнопки" добавить еще один запрос данных "и" удалить ".

Я сделалвложенная форма в другом компоненте моего приложения, но я изо всех сил пытаюсь понять, как использовать пример из среднего поста для включения вложенной формы (в качестве повторяемого элемента - то есть кто-то может захотеть 5 запросов данных).

Есть ли примеры того, как это реализовать?

В своем коде я в основном следовал за средним постом, но пытался связать компонент формы запроса данных внутри индекса

<button 
      type="button"
      onClick={() => arrayHelpers.insert(index, <DataRequestForm />)}>
      Add a data request
</button>   

Это явно неверно, но я не могу получитьручка о том, как это сделать.

Принимая ответ Нитина, я попытался изменить встроенную форму, чтобы я мог использовать response-select, как показано ниже, но я получаю сообщение об ошибке:

TypeError: Невозможно прочитать свойства 'values' из неопределенного

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;

Ответы [ 2 ]

3 голосов
/ 13 октября 2019

Вы не можете добавлять вложенные формы внутри элемента формы . Пожалуйста, обратитесь к приведенному ниже сообщению для получения подробной информации о режиме.

Можете ли вы вложить HTML-формы?

Если вы хотите вложить несколько полей с вложенной структурой в основную формуВы можете достичь этого, используя 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 обновить ключ requestField1 из nth элемент массива dataRequests со значением поля ввода.

Наконец, ваш <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 snippet

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>
1 голос
/ 22 октября 2019

Для тех, кто хочет извлечь уроки из этого поста, ответ на этот вопрос от нитина явно мотивирован благими намерениями, но это неправильное использование formik. Вы можете проверить изолированную программную среду кода здесь: https://codesandbox.io/embed/goofy-glade-lx65p?fontsize=14 для текущей попытки решения этой проблемы (все еще не решенной), но лучший шаг к решению. Спасибо вам за полезные намерения, стоящие за ответом на этот вопрос.

...