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!