Roshan
Roshan

Reputation: 3384

How to get values from react FieldArray in formik form with other fields?

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.

Upvotes: 7

Views: 18712

Answers (2)

ToolmakerSteve
ToolmakerSteve

Reputation: 21223

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.

Upvotes: 2

Israel kusayev
Israel kusayev

Reputation: 1001

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.

Upvotes: 0

Related Questions