Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Formik Validation in a Field Array

Tags:

reactjs

formik

I have a formik form like this:-

import Drawer from "components/atoms/Drawer";
/* import Input from "components/atoms/Input";
import InputGroup from "components/atoms/InputGroup";
import Label from "components/atoms/Label"; */
import Scrim from "components/atoms/Scrim";
import DrawerBody from "components/molecules/DrawerBody";
import { Field, FieldArray, Form, FormikErrors, FormikProps, withFormik } from "formik";
import { ITrackedPage } from "hocs/withAppDynamics";
import * as React from "react";
import { Box, Flex, Text } from "rebass";
import * as AmenitiesActions from "store/amenities/actions";
import { IAmenity, IAmenityRanking } from "store/amenities/models";
import DrawerHeader from "./DrawerHeader";
// import ErrorMessage from 'components/atoms/ErrorMessage';




interface IAmenitiesDrawerProps {
    drawerOpen: boolean;
    onDrawerClose: () => void;
    tenantAssessmentId: string;
    actions: typeof AmenitiesActions;
    maxRank?: number;
  }



interface IAmenitiesDrawerValues {
    amenitieslist: IAmenity[];
}

const InnerForm: React.FC<
IAmenitiesDrawerProps & ITrackedPage & FormikProps<IAmenitiesDrawerValues>
> = ({
  errors,
  drawerOpen,
  onDrawerClose,
  handleChange,
  values,
  setValues,
  isValid,
  tenantAssessmentId,
  sendAnalyticsData,
  actions
}) => {

  const handleDrawerClose = () => {
    onDrawerClose();
  };

  return (
    <>
      <Scrim isOpen={drawerOpen} onClose={handleDrawerClose} />
      <Drawer isOpen={drawerOpen} direction="right" drawerWidth="700px">
        <DrawerHeader handleCloseDrawer={handleDrawerClose} />
        <DrawerBody p={5}>
          <Flex mb={4}>
            <Box flex={1}>
              <Text fontWeight="light" fontSize={4} mt={3} mb={4}>
                Add custom amenities
              </Text>
            </Box>
          </Flex>
          <Form>
          <FieldArray
              name="amenitieslist"
              render={arrayHelpers => (
                <div>
                {// values.amenitieslist && values.amenitieslist.length > 0 ? (
                  values.amenitieslist.map((amenity, index) => (
                    <div key={index}>
                      <Field name={`amenitieslist.${index}.name`} />
                      <button
                        type="button"
                        onClick={() => arrayHelpers.remove(index)} // remove a amenity from the list
                      >
                        -
                      </button>
                      {errors.amenitieslist}
                    </div>
                  ))}
                  <button type="button" onClick={() => arrayHelpers.push({ id: "", name: "", imageUrl: '', description: '', tenantAssessmentId })}>
                    {/* show this when user has removed all amenities from the list */}
                    Add a Amenity
                  </button>
              </div>
            )}
            />
            <div>
                <button type="submit">Submit</button>
              </div>
    </Form>
        </DrawerBody>
      </Drawer>
    </>
  );
};

export const AmenitiesDrawer = withFormik<IAmenitiesDrawerProps, IAmenitiesDrawerValues>({
  enableReinitialize: true,
  handleSubmit: (values, {props}) => {
    const seed: number = props.maxRank? props.maxRank : 0;
    const amenityRankings: IAmenityRanking[] = values.amenitieslist.map((a, index)=>({
        amenityId: 1,
        rank: index + 1 + seed,
        amenityName: a.name,
        customAmenity: true
      }));
      console.log(amenityRankings);
      console.log(props.actions.customSaveAmenities);
      console.log(props.tenantAssessmentId);
      props.actions.customSaveAmenities(props.tenantAssessmentId, amenityRankings);
  },
  mapPropsToValues: ({tenantAssessmentId}) => ({
      amenitieslist:[{id: 0, name: '', imageUrl: '', description: '', tenantAssessmentId}]
  }),
  validate: values => {
    const errors: FormikErrors<{ validAmenity: string }> = {};
    console.log('In the Validate method');
    const { amenitieslist } = values;

    const amenityValid = amenitieslist[0].name.length < 28;
    if (!amenityValid) {
        console.log('Amenity is not valid');
      errors.validAmenity = "Amenity needs to be atmost 28 characters";
      console.log(errors);
    }

    return errors;
  }
})(InnerForm);

As you all can see I have a text input. I want to throw a error message below the text field when the length is more than 28 characters long.

How is this possible ? Please help me with this.

like image 793
SaiBand Avatar asked Sep 14 '19 17:09

SaiBand


1 Answers

I find the most convenient way to validate Formik forms is using yup as recommended in their documentation. You can define a validation schema and pass it as a prop to the main Formik component (or HOC as it appears you're using) and remove your custom validation function:

validationSchema: yup.object().shape({
  amenitieslist: yup.array()
    .of(yup.object().shape({
      name: yup.string().max(28, "Max 28 chars")
      // Rest of your amenities object properties
    }))
})

And then in your FieldArray:

<FieldArray
    name="amenitieslist"
    render={arrayHelpers => (
        <div>
          { values.amenitieslist && values.amenitieslist.length > 0 ? (
            values.amenitieslist.map((amenity, index) => (
                <div key={index}>
                  <Field name={`amenitieslist[${index}].name`} />
                  <button
                      type="button"
                      onClick={() => arrayHelpers.remove(index)} // remove a amenity from the list
                  >
                    -
                  </button>
                  {errors.amenitieslist[index].name}
                </div>
            ))}
          <button type="button" onClick={() => arrayHelpers.push({ id: "", name: "", imageUrl: '', description: '', tenantAssessmentId })}>
            {/* show this when user has removed all amenities from the list */}
            Add a Amenity
          </button>
        </div>
    )}
/>

I just changed the accessors you were using to designate the field name (to use an index for an array element, you have to use bracket notation) and where to find the errors, yup should generate them automatically. Tough to know for sure I'm not missing anything without testing it, hope this helps!

like image 109
Chris B. Avatar answered Oct 21 '22 08:10

Chris B.