Skip to content
Advertisement

How to set Formik FieldArray select options at various index values based on another selection

I am using a Formik FieldArray that consists of the following fields:

Pet Type
Pet Food

My initialValues setup for this FieldArray is as follows:

const myPetGroup = {
      petDetails: [
        {
          petSelection: [
            {
              petType: "",
              petFood: ""
            }
          ]
        }
      ]
};

const INITIAL_FORM_STATE = {
  allPets: [myPetGroup]
}

Based on the selection value for Pet Type, I need to assign a specific set of options to Pet Food. Obviously I need to make sure that values are maintained at each row index level.

Assuming I have the following options for Pet Type

const petListOptions = [
  { value: "dog", label: "Dog" },
  { value: "cat", label: "Cat" }
];

The following are the two types of food options bases on pet selection:

const dogFood = [
  { value: "dog food choice a", label: "Dog Food A" },
  { value: "dog food choice b", label: "Dog Food B" }
]; 

const catFood = [
  { value: "cat food a", label: "Cat Food A" },
  { value: "cat food b", label: "Cat Food B" }
];

Assuming my FieldArray index is at 0:

<Select
  name={`petType.${index}`}
  options={petListOptions}
  value={dog}
/>

Based on Pet Type selection of dog how can I then set the following select to have the options of dogFood at index 0, that is:

<Select
  name={`petFood.${index}`}
  options={dogFood}
/>

Now if the user adds another row within the FieldArray, now at index 1 and now select cat, how can I set the options to now use catFood at index 1 ?

<Select
  name={`petType.${index}`}
  options={petListOptions}
  value={cat}
/>

<Select
  name={`petFood.${index}`}
  options={catFood}
/>

I am not sure how to set these Pet Food options at various indexes based on Pet List selection within my FieldArray.

Advertisement

Answer

So, to understand correctly, you have to render a list of controls, one that makes selection of which type of pet and then based off of the first selection, the program renders the food options to choose from.

const petListOptions = [
  { value: "dog", label: "Dog" },
  { value: "cat", label: "Cat" }
];

The value selected from the first list would determine what options to render next. Therefore as an ease for retrival I have kept the key of the petFood array below to match the set set of possible values returned from first selection above.

const petFood = {
  dog: [
    { value: "dog food choice a", label: "Dog Food A" },
    { value: "dog food choice b", label: "Dog Food B" }
  ],
  cat: [
    { value: "cat food a", label: "Cat Food A" },
    { value: "cat food b", label: "Cat Food B" }
  ]
};

To save the values of petType and petFood we pass an array with the first object initialized where both petType and food properties are set to empty. This will render out just one set of row(s).

In the code below : <Field name={petSelection.${index}.petType} as="select" value={selectedPet.petType}> ... </Field> tells formik of how to store the value of the selection. Notice the name property on FieldArray. The property where you would want to store the value should then be FieldArray's name <dot> Index. If we want to place our value in a specific property, we can append <dot> Property name to the name of our Select Field.

export const PetsList = () => (
  <div>
    <h1>Pet List</h1>
    <Formik
      initialValues={{
        petSelection: [
          {
            petType: "",
            petFood: ""
          }
        ]
      }}
      onSubmit={(values) =>
        setTimeout(() => {
          alert(JSON.stringify(values, null, 2));
        }, 500)
      }
      render={({ values }) => (
        <Form>
          <FieldArray
            name="petSelection"
            render={(arrayHelpers) => (
              <div>
                {values.petSelection && values.petSelection.length > 0 ? (
                  values.petSelection.map((selectedPet, index) => {
                    //console.log("selectedPet" + index, selectedPet);
                    return (
                      <div key={index}>
                        <Field
                          name={`petSelection.${index}.petType`}
                          as="select"
                          value={selectedPet.petType}
                        >
                          <option value="" disabled />
                          {petListOptions.map((pet, petIndex) => (
                            <option value={pet.value}>{pet.label}</option>
                          ))}
                        </Field>
                        {selectedPet.petType && (
                          <Field
                            name={`petSelection.${index}.petFood`}
                            as="select"
                            value={selectedPet.petFood}
                          >
                            <option value="" disabled />
                            {petFood[selectedPet.petType].map(
                              (food, foodIndex) => (
                                <option value={food.value}>{food.label}</option>
                              )
                            )}
                          </Field>
                        )}
                        <button
                          type="button"
                          onClick={() => arrayHelpers.remove(index)} // remove a friend from the list
                        >
                          -
                        </button>
                        <button
                          type="button"
                          onClick={() =>
                            arrayHelpers.insert(index, {
                              petType: "",
                              petFood: ""
                            })
                          } // insert an new object at a position
                        >
                          +
                        </button>
                      </div>
                    );
                  })
                ) : (
                  <button 
                      type="button" 
                      onClick={() => arrayHelpers.push({
                          petType: "",
                          petFood: ""
                      })}>
                    {/* show this when user has removed all pets from the list */}
                    Add a Pet
                  </button>
                )}
                <div>
                  <button type="submit">Submit</button>
                </div>
              </div>
            )}
          />
        </Form>
      )}
    />
  </div>
);

the add button just simply adds a new object to the petsSelection array with properties petType and petFood initialized to empty string. Now at any row you make a selection, you are sure to add the values to this new object!

User contributions licensed under: CC BY-SA
9 People found this is helpful
Advertisement