7

I have created a Formik form that contains a field array, form and fieldArray is in two separate classes as separate components.

My form:

<Formik onSubmit = {(values, { setSubmitting }) => { setSubmitting(false);}}
        enableReinitialize>
    {({handleSubmit, errors})=> (
        <Form onSubmit= { handleSubmit }>
            <Form.Group as= { Row } controlId= "cpFormGroupTitle" className="required">
                <Form.Label className="post-create-label"  column sm={ 2 } >
                    Title
                </Form.Label>
                <Col sm={ 10 }>
                    <Field name="title" component={ renderTextField } type="text"
                           isinvalid={ !!errors.title ? "true": "false" }
                           placeholder="Title *" />
                </Col>
            </Form.Group>
            <Form.Group as= { Row } controlId= "cpFrmGroupShortDesc"  className="required">
                <Form.Label className="post-create-label"  column sm={ 2 } >
                    Short Description
                </Form.Label>
                <Col sm={ 10 }>
                    <Field name="short-desc" component={ renderTextArea } type="text"
                           isinvalid={ !!errors.shortDescription ? "true": "false" }
                           placeholder="Short Description *" />
                </Col>
            </Form.Group>
            <Form.Group as= { Row } controlId= "cpFormGroupFeatures">
                <Form.Label className="post-create-label"  column sm={ 2 }>
                    Features
                </Form.Label>
                <Col sm={ 10 }>
                    <TextFieldArray initialValues={{ features: [] } } name="features"/>
                </Col>
            </Form.Group>
            <Form.Group as={ Row }>
                <Col sm= { { span: 2, offset:2 } }>
                    <Button type="submit"  variant="primary">Submit</Button>
                </Col>
                <Col sm={ 2 }>
                    <Button variant="secondary">Save as draft</Button>
                </Col>
            </Form.Group>
        </Form>
    )}
</Formik>

Here, <TextFieldArray> is field array , I need to get values from field array when form is submitted.

TextFieldArray:

export const TextFieldArray = (props) => (
    <React.Fragment>
        <Formik initialValues= { props.initialValues } render={({ values }) => (
            <Form>
                <FieldArray name= { props.name } render={arrayHelper => (
                    <div>
                        { values[props.name] && values[props.name].length > 0 ?
                            (
                                values[props.name].map((item, index) => (
                                    <div key={index}>
                                        <Form.Group as= { Row }>
                                            <div className="col-md-8">
                                                <Field name={`${props.name}.${index}`}
                                                       className="form-control"/>
                                            </div>
                                            <div className="col-md-2">
                                                <Button type="button" variant="outline-secondary"
                                                        onClick={() => arrayHelper.remove(index)}>
                                                    Remove
                                                </Button>
                                            </div>
                                            <div className="col-md-2">
                                                <Button type="button" variant="outline-secondary"
                                                    onClick={() => arrayHelper.insert(index, '')}>
                                                    Add
                                                </Button>
                                            </div>
                                        </Form.Group>
                                    </div>
                                ))
                            ) : (
                                <Button type="button" variant="outline-secondary"
                                        onClick={() => arrayHelper.push('')} >
                                   {`Add ${ props.name }`}
                                </Button>
                            )
                        }
                    </div>
                )} />
            </Form>
        )} />
    </React.Fragment>
);

I'm a beginner to ReactJS, so someone help me please, that will be huge help from you all. Thanks.

2 Answers 2

2

To have field array be part of same form as other fields, only have one <Formik> and one <Form>. Then make initialValues on Formik that describes all the fields:

<Formik
  initialValues={{ friends: someFriends, random: randomText }}

As seen in the following code from Formik FieldArray docs, with another form field added that is not part of the array:

import React, { useState } from "react";
import ReactDOM from "react-dom";
import { Formik, Form, Field, useField, FieldArray } from "formik";

const someFriends = ["jared", "ian", "brent"];
const randomText = "Four score and seven years ago...";

function MyTextInput({ label, ...props }) {
  // useField() returns [formik.getFieldProps(), formik.getFieldMeta()]
  // which we can spread on <input> and alse replace ErrorMessage entirely.
  const [field, meta] = useField(props);
  return (
    <>
      <label
        htmlFor={props.id || props.name}
        css={{ backgroundColor: props.backgroundColor }}
      >
        {label}
      </label>
      <input className="text-input" {...field} type="text" {...props} />
      {meta.touched && meta.error ? (
        <div className="error">{meta.error}</div>
      ) : null}
    </>
  );
}

// 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: someFriends, random: randomText }}
      onSubmit={values =>
        setTimeout(() => {
          alert(JSON.stringify(values, null, 2));
        }, 500)
      }
      render={({ values }) => (
        <Form>
          <MyTextInput label="Random comment" name="random" />
          <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>
                  ))}

                {/* Add a new empty item at the end of the list */}
                <button type="button" onClick={() => arrayHelpers.push("")}>
                  Add Friend
                </button>

                <div>
                  <button type="submit">Submit</button>
                </div>
              </div>
            )}
          />
        </Form>
      )}
    />
  </div>
);

ReactDOM.render(<FriendList />, document.getElementById("root"));

Code in codesandbox.

Sign up to request clarification or add additional context in comments.
0

I don't think you need to create second form for child component.
You need to just pass the values from the parent to the TextFieldArray

    <TextFieldArray values={values.myArr} name="features"/>     

And the child component just receive the values and render them (as if it was in the parent component)

export const TextFieldArray = (props) => {
return (
    <React.Fragment>
                    <FieldArray
                        name= { props.name }
                        render={arrayHelper => (
                            <div>
                                {
                                    props.values[props.name] && props.values[props.name].length > 0 ?
                                        (
                                            props.values[props.name].map((item, index) => (
                                                <div key={index}>
                                                    <Form.Group as= { Row }>
                                                        <div className="col-md-8">
                                                            <Field name={`${props.name}.${index}`} className="form-control"/>
                                                        </div>
                                                        <div className="col-md-2">
                                                            <Button type="button" variant="outline-secondary"
                                                                onClick={() => arrayHelper.remove(index)}>
                                                                Remove
                                                            </Button>                                                              
                                                        </div>
                                                        <div className="col-md-2">
                                                            <Button type="button" variant="outline-secondary"
                                                                onClick={() => arrayHelper.insert(index, '')}
                                                            >
                                                                Add
                                                            </Button> 
                                                        </div>
                                                    </Form.Group>
                                                </div>
                                            ))
                                        ) : (
                                            <Button type="button" variant="outline-secondary" onClick={() => arrayHelper.push('')} >
                                               {`Add ${ props.name }`}
                                            </Button>
                                        )
                                }
                            </div>
                        )}
                    />
    </React.Fragment>
)

Of course don't forget to add the initial values of the array to the parent component.

And finally when you click on the submit button it would give you the values.

1 Comment

but clicking add button does not add new values to array in TextFieldArray !

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.