DEV Community

Cover image for Using setErrors for with Formik DOM, useFormikContext
Antolin B. Bernas Jr.
Antolin B. Bernas Jr.

Posted on • Updated on

Using setErrors for with Formik DOM, useFormikContext

TL;DR Hi! everyone in this article I would like to share you about using of setErrors within the Formik DOM, this is for everyone who struggling 😫😫 of setting an error messages from the backend request.

In this scenario, I used useField for separating of Fields from Form

Follow this proceedures step by step 😊

1.) First of all, make sure you have installed the following dependencies.

Formik
npm install Formik --save

Yup
npm install Formik --save
Enter fullscreen mode Exit fullscreen mode

2.) Second, Create a component for custom formik as HOC

Image description

3.) Third, follow this code:

3.1.) Import the following dependencies:

import { useEffect, Fragment } from "react";
import { useFormikContext } from "formik";
import { Formik } from "formik";
Enter fullscreen mode Exit fullscreen mode

Since we are using Formik DOM, so we are able to use useFormikContext. Because, its easy to read and effective separation of code

3.2.) Using a Component for setErrors of Formik library

This component is responsible for receiving a prop of error messages (setErrors) and setting an errors from our backend request that we want to show on our form or within the fields.

I used useEffect as watcher of setErrors prop for every time the errors from backend request has changes.

I used useFormikContext to have access in setErrors from the Formik Provider as Formik component.

const FormikWithSetErrors = ({ children, setErrors })  => {

    const { setErrors:setBackendErrors } = useFormikContext();

    useEffect(()=>{
        if(setErrors) setBackendErrors(setErrors);
    },[setErrors]);

    return <Fragment>{children}</Fragment>;
}
Enter fullscreen mode Exit fullscreen mode

3.3.) Get values from Formik provider

I wrapped the Formik component while our FormikWithSetErrors is our child component. To access the formik provider's value.

const FormikForm = ({ children, setErrors, ...otherProps }) => {
    return (
        <Formik {...otherProps}>
            <FormikWithSetErrors setErrors={setErrors}>
                {children}
            </FormikWithSetErrors>
        </Formik> 
    );
}
Enter fullscreen mode Exit fullscreen mode

Formik/index

import { useEffect, Fragment } from "react";
import { useFormikContext } from "formik";
import { Formik } from "formik";

const FormikWithSetErrors = ({ children, setErrors })  => {

    const { setErrors:setBackendErrors } = useFormikContext();

    useEffect(()=>{
        if(setErrors) setBackendErrors(setErrors);
    },[setErrors]);

    return <Fragment>{children}</Fragment>;
}

const FormikForm = ({ children, setErrors, ...otherProps }) => {
    return (
        <Formik {...otherProps}>
            <FormikWithSetErrors setErrors={setErrors}>
                {children}
            </FormikWithSetErrors>
        </Formik> 
    );
}

export default FormikForm;

Enter fullscreen mode Exit fullscreen mode

For UI

I used Material-UI for faster demonstration and easy frontend development

I made at least two component for sample form fields that we will use to demonstrate the Formik.

  ./components
   ../Button/index.js
   ../TextField/index.js
Enter fullscreen mode Exit fullscreen mode

Here's the snippet code of two components:

Button/index.js

import { useFormikContext } from "formik";
import { Button } from "@material-ui/core";

const FormButton = ({ children, otherProps }) => {
  const { submitForm } = useFormikContext();

  const handleSubmit = () => {
    // this will trigger the <Formik> prodiver
    submitForm();
  };

  const configFormButton = {
    ...otherProps,
    variant: "contained",
    color: "primary",
    fullWidth: true,
    onClick: handleSubmit
  };

  return <Button {...configFormButton}>{children}</Button>;
};

export default FormButton;
Enter fullscreen mode Exit fullscreen mode

TextField/index.js

import { TextField } from "@material-ui/core";
import { useField } from "formik";

const FormTextField = ({ name, ...otherProps }) => {
  const [field, meta] = useField(name);

  const configFormTextField = {
    ...field,
    ...otherProps,
    variant: "outlined",
    fullWidth: true,
    size: "small"
  };

  if (meta && meta.error) {
    configFormTextField.error = true;
    configFormTextField.helperText = meta.error;
  }

  return <TextField {...configFormTextField} />;
};

export default FormTextField;
Enter fullscreen mode Exit fullscreen mode

I created a validation schema for client validation use. Using a library Yup

  ./components
     ../validationSchema.js
Enter fullscreen mode Exit fullscreen mode

validationSchema.js

import * as Yup from "yup";

const ValidationSchema = Yup.object().shape({
  firstName: Yup.string().required("First Name is required")
});

export default ValidationSchema;
Enter fullscreen mode Exit fullscreen mode

I created a fakeBackend hook request using setTimeout, just to simulate the producing of error response message.

  ./components
     ../fakeBackend.js
Enter fullscreen mode Exit fullscreen mode

fakeBackend.js

import { useState } from "react";

const useFakeBackend = () => {
  const [errors, setErrors] = useState({});

  // simulating a backend request
  const setRequest = () => {
    setTimeout(() => {
      setErrors((errors) => ({
        firstName: "Please enter a valid First Name "
      }));
    }, 2000);
  };

  return { errors, setRequest };
};

export default useFakeBackend;
Enter fullscreen mode Exit fullscreen mode

Finally!!!! Now were almost there!! 😊😊😊

This is the final setup of our created components for simulation

App.js

import { Grid } from "@material-ui/core";
import Formik from "./components/Formik/index";
import Button from "./components/Button/index";
import TextField from "./components/TextField/index";
import ValidationSchema from "./components/validationSchema";
import useFakeBackend from "./components/fakeBackend";

export default function App() {
  const { errors, setRequest } = useFakeBackend();

  return (
    <Formik
      initialValues={{ firstName: "" }}
      validationSchema={ValidationSchema}
      enableReinitialize={true}
      setErrors={errors}
      onSubmit={() => {
        alert("backend requesting...");
        setRequest();
      }}
    >
      <Grid container spacing={2}>
        <Grid item xs={12}>
          <label>First Name</label>
        </Grid>
        <Grid item xs={12}>
          <TextField name="firstName" />
        </Grid>
        <Grid item xs={12}>
          <Button>Submit</Button>
        </Grid>
      </Grid>
    </Formik>
  );
}

Enter fullscreen mode Exit fullscreen mode

Now, our UI form is now done!!!

Image description

You can fork the whole sample project here:
https://codesandbox.io/s/formik-context-jsoe0?file=/src/App.js

That's all thank you guys!!! 😊😊😊

Top comments (0)