Manusha Karunathilaka
Manusha Karunathilaka

Reputation: 139

How to manage nested complex objects using Formik

I used Formik library to deal with a nested object. So, I tried FormArray inside FormArray but it is not correctly rendering. How can I fix that?

Tried various ways to handle the nested object that I had but the issue was Field is not rendering as I wished.

import React from "react";
import { Formik, Form, Field, FieldArray } from "formik";

// 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.
const FriendList = () => (
  <div>
    <h1>Friend List</h1>
    <Formik
      initialValues={{ 
        subSkillName: "",
        queries: [
            {
                query: "",
                intent: '',
                entities: [{

                    entityName: "",
                    entityValue: '',


                }]

            }
        ] }}
      onSubmit={values =>
        setTimeout(() => {
          alert(JSON.stringify(values, null, 2));
        }, 500)
      }
      render={({ values }) => (
        <Form>
            <Field name='subSkilName' placeholder='subskillname'></Field>
          <FieldArray
            name="queries"
            render={arrayHelpers => (
              <div>
                {values.queries && values.queries.length > 0 ? (
                  values.queries.map((friend, index) => (
                    <div key={index}>
                        <label>subskillname</label>
                      <Field name={`queries.${index}.query`} placeholder='query'/>
                      <Field name={`queries.${index}.intent`} placeholder='intent'/>

                      <FieldArray name={`queries.${index}.entities`}
                      render={arrayHelpers => (
                          <div>
                               {(values.queries[index]).entities && (values.queries[index]).entities.length > 0 ? (
                                  (values.queries[index]).entities.map((entity, entityindex) => {
                                    <div key={entityindex}>
                                        {console.log('came to the entity')}

                                       <Field name={`queries.${index}.entities.${entityindex}.entityName`} ></Field>

                                   </div>
                                  })

                               ):(<button type="button" onClick={() => arrayHelpers.push({

                                entityName: "",
                                entityValue: '',


                            }
                        )}>
                        {/* show this when user has removed all friends from the list */}
                        Add a queries
                      </button>) }
                          </div>


                      )}
                      >


                      </FieldArray>

                      <button
                        type="button"
                        onClick={() => arrayHelpers.remove(index)} // remove a friend from the list
                      >
                        -
                      </button>
                      <button
                        type="button"
                        onClick={() => arrayHelpers.push({


                                    query: "",
                                    intent: '',
                                    entities: []

                                }
                            )} // insert an empty string at a position
                      >
                        +
                      </button>
                    </div>
                  ))
                ) : (
                  <button type="button" onClick={() => arrayHelpers.push({

                            query: "",
                            intent: '',
                            entities: []

                        }
                    )}>
                    {/* show this when user has removed all friends from the list */}
                    Add a queries
                  </button>
                )}
                {/* <div>
                  <button type="submit">Submit</button>
                </div> */}
              </div>
            )}
          />
           <div>
                  <button type="submit">Submit</button>
                </div>
        </Form>
      )}
    />
  </div>
);

export default FriendList;

Here I wanted to display the entityName (queries>>entities>>entityName) but it will not render that Filed. How can I access the array inside another array by using Formik?

Upvotes: 6

Views: 5454

Answers (1)

Qalib Abbas
Qalib Abbas

Reputation: 51

Here is a working example Your code is fine just need to rename the second parament(index) of map function

    <div className="at-createsurvey">
        <Formik
          enableReinitialize
          initialValues={{
            surveyName: "",
            description: "",
            startDate: Date.now(),
            endDate: Date.now(),
            questions: [
              {
                title: "",
                questionType: "ShortAnswer",
                answers: [
                  {
                    text: "",
                  },
                ],
              },
            ],
          }}
          onSubmit={(values) => console.log(values)}
          validationSchema={surveySchema}
        >
          {({ values, handleSubmit, handleChange, setValues }) => (
            <form>
              <div className="at-container">
                <div className="at-pageheading">
                  <div className="at-title">
                    <Link
                      to="/tribe-details"
                      className="at-roundicon at-backbtn"
                    >
                      <i className="icon-angle-left"></i>{" "}
                    </Link>
                    <h2>back</h2>
                  </div>
                  <div className="at-right">
                    <div className="at-datearae">
                      <div className="at-datebox">
                        <span>Start date</span>
                        <DatePicker
                          selected={startDate}
                          onChange={(date) => setStartDate(date)}
                        />
                      </div>
                      <div className="at-datebox">
                        <span>End date</span>
                        <DatePicker
                          selected={endDate}
                          onChange={(date) => setEndDate(date)}
                        />
                      </div>
                    </div>
                  </div>
                </div>
                <form className="at-themeform at-surveyform">
                  <div className="at-surveycontent">
                    <div className="at-surveyname-box">
                      <input
                        type="text"
                        className="form-control"
                        placeholder="Survey name type here..."
                        name="surveyName"
                        onChange={handleChange}
                        //   value={values.surveyName}
                      />
                      <textarea
                        className="form-control"
                        placeholder="description"
                        name="description"
                        onChange={handleChange}

                        //   value={values.description}
                      ></textarea>
                    </div>

                    <div className="at-surveyquestion-area">
                      <FieldArray name="questions">
                        {(arrayHelper) => (
                          <div className="at-heading">
                            <button
                              type="button"
                              className="at-roundicon"
                              onClick={() => {
                                arrayHelper.push({
                                  title: "",

                                  answers: [
                                    {
                                      text: "",
                                    },
                                  ],
                                  questionType: "SingleChoice",
                                });
                              }}
                            >
                              <i className="icon-plus"></i>
                            </button>
                            <h3>Add New Question</h3>
                          </div>
                        )}
                      </FieldArray>

                      {values.questions.map((question, i) => (
                        <div className="at-surveyquestion-box">
                          <input
                            type="text"
                            className="form-control"
                            placeholder="Question..."
                            name={`questions.${i}.title`}
                            onChange={handleChange}
                          />

                          <ul
                            className={`at-surveyoption ${getClassName(
                              question.questionType
                            )}`}
                          >
                            {question.answers.map((answer, index) => (
                              <li key={index}>
                                <div className="at-inputbox">
                                  <Answer
                                    name={`questions.${i}.answers.${index}.text`}
                                    value={answer.text}
                                    answerType={question.questionType}
                                    onChange={handleChange}
                                  />
                                </div>
                                <button type="button" 
                                  className="at-closebtn" 
                                  onClick={() => {
                                    values.questions[i].answers.splice(index, 1);
                                    setValues(values);
                                  }}
                                 >
                                  <i className="icon-close"></i>
                                </button>
                              </li>
                            ))}
                          </ul>

                          <button
                            type="button"
                            className="at-addbtn"
                            onClick={() => {
                              question.answers.push({ text: "" });
                              setValues(values);
                            }}
                          >
                            <i className="icon-plus"></i> add option
                          </button>

                          <div className="at-actionarea">
                            <div className="at-select">
                              <select
                                name={`questions.${i}.questionType`}
                                value={question.questionType}
                                onChange={handleChange}
                              >
                                <option value="SingleChoice">
                                  Single Choice
                                </option>
                                <option value="MultipleChoice">
                                  Multiple Choice
                                </option>
                                <option value="ShortAnswer">
                                  Short Answer
                                </option>
                              </select>
                            </div>
                            <div className="at-actionbox">
                              <button type="button">
                                <i className="icon-copy"></i>
                              </button>
                              <button
                                type="button"
                                onClick={() => {
                                  values.questions.splice(i, 1);
                                  setValues(values);
                                }}
                              >
                                <i className="icon-delet"></i>
                              </button>
                            </div>
                          </div>
                        </div>
                      ))}
                    </div>
                  </div>
                  <div className="at-surveydateview">
                    <div className="at-head">
                      <h3>1 Question</h3>
                    </div>
                    {/* <figure className="at-nosurveyimg">
                                      <img src={images.nosurveydata} alt="images description"/>
                                  </figure>
                                  <span>Create your new survey here</span> */}
                    <div className="at-surveyquestion-data">
                      {JSON.stringify(values, null, 2)}
                      <h3>1) Please select your age group</h3>
                      <ul className="at-surveyquestion-list">
                        <li>
                          <label className="at-customcheckbox">
                            <input type="checkbox" className="form-control" />
                            <em className="at-checkmark"></em>
                          </label>
                          <span>Under 18</span>
                        </li>
                        <li>
                          <label className="at-customradio">
                            <input
                              type="radio"
                              name="radio"
                              className="form-control"
                            />
                            <em className="at-checkmark"></em>
                          </label>
                          <span>option1</span>
                        </li>
                        <li>
                          <label className="at-customradio">
                            <input
                              type="radio"
                              name="radio"
                              className="form-control"
                            />
                            <em className="at-checkmark"></em>
                          </label>
                          <span>option2</span>
                        </li>
                        <li>
                          <input
                            type="text"
                            className="form-control"
                            placeholder="Short Line Answer"
                          />
                        </li>
                      </ul>
                    </div>
                    <div className="at-btnarea">
                      <button type="button" className="at-btn">
                        Publish
                      </button>
                      <button type="button" className="at-btn green">
                        Save
                      </button>
                      {/* <button
                        type="button"
                        className="at-btn red"
                        onClick={handleShow}
                      >
                        Delete
                      </button> */}
                    </div>
                  </div>
                </form>
              </div>
            </form>
          )}
        </Formik>
      </div>

Edit: Added OnClick action to remove questions and answers from their index using Array.splice() instead of Array.pop()

Upvotes: 5

Related Questions