Dealing with forms in React applications used to be tedious, but with libraries like Formik and Yup, handling form state and validating each field is much easier and efficient. Formik does the overall form management and Yup helps to define the structure and rules to be applied to the form.
When working with file inputs, schema building and validation is not as straightforward as validating a text field, for example. This guide will show how to implement that in a React application.
Step 1: Installing Dependencies
To install Formik and Yup, run this on your command line:
npm install formik yup
If you're using yarn, do this instead:
yarn add formik yup
Step 2: Handling Form State
The form should consist of a field with an input of type 'file' and a submit button. At this point, the form looks like this:
const MyForm = () => {
return (
<form>
<input type='file' name='myFile' accept='.pdf'/>
<button type='submit'>Submit Form</button>
</form>
)
}
export default MyForm;
There are different ways to handle form state with Formik, but in this guide we'll be using the useFormik
hook to directly provide all the Formik helpers needed. Here we can define the initial values of the input field, what happens when the form is submitted, and a validation schema.
import { useFormik } from 'formik';
const MyForm = () => {
const formik = useFormik({
initialValues: {
myFile: ''
},
onSubmit: () => {
console.log('Form submitted!')
}
})
return (
<form>
<input type='file' name='myFile' accept='.pdf'/>
<button type='submit'>Submit Form</button>
</form>
)
}
export default MyForm;
Step 3: Building the Validation Schema
Next, import Yup into the file to define the set of rules you want the form field to obey. In this guide, we'll be validating the file input in two aspects:
- The field must be required
- The file must only accept PDF files
- The file size must not be more than 3 megabytes
Here's what the validation schema looks like:
import * as Yup from 'yup';
const validationRules = Yup.object().shape({
myFile: Yup.mixed().required('required')
.test('fileFormat', 'Only PDF files are allowed', value => {
if (value) {
const supportedFormats = ['pdf'];
return supportedFormats.includes(value.name.split('.').pop());
}
return true;
})
.test('fileSize', 'File size must be less than 3MB', value => {
if (value) {
return value.size <= 3145728;
}
return true;
}),
})
When validating the file type, we use the test
function to determine whether the file extension is .pdf
. We also have to test if the file size is not more than 3,145,728 bytes (3 megabytes).
Step 4: Displaying Errors in Field
We intend to display the validation errors below the field, so we render a paragraph just below the file input:
<form onSubmit={formik.handleSubmit}>
<input type='file' name='myFile' accept='.pdf' onChange={handleChange}/>
<div>{(formik.errors.myFile) ? <p style={{color: 'red'}}>{formik.errors.myFile}</p> : null}</div>
<br/>
<button type='submit'>Submit Form</button>
</form>
To make sure that Formik is actually in charge of the field's state, a function is passed to the input's onChange
property. The function sets the value of the field to Formik's state by using the helper function setFieldValue
, a function Formik provides to set a field's value to something. It takes two arguments: the name of the field and the value to be set. The onChange
function looks like this:
const handleChange = (e) => {
formik.setFieldValue('myFile', e.target.files[0]);
};
The result of this is that the file is validated every time its value changes and when the form is submitted. Also, Formik's handleSubmit
helper function is passed into the form element's onSubmit
property. This will trigger Formik's own onSubmit
function.
Here's the complete code:
import React from "react";
import { useFormik } from "formik";
import * as Yup from 'yup';
const MyForm = () => {
const validationRules = Yup.object().shape({
myFile: Yup.mixed().required('required')
.test('fileFormat', 'Only PDF files are allowed', value => {
if (value) {
const supportedFormats = ['pdf'];
return supportedFormats.includes(value.name.split('.').pop());
}
return true;
})
.test('fileSize', 'File size must not be more than 3MB',
value => {
if (value) {
return value.size <= 3145728;
}
return true;
}),
})
const formik = useFormik({
initialValues: {
myFile: ''
},
onSubmit: () => {
console.log('Submitted')
},
validationSchema: validationRules,
})
const handleChange = (e) => {
formik.setFieldValue('myFile', e.target.files[0]);
};
return (
<form onSubmit={formik.handleSubmit}>
<input type='file' name='myFile' accept='.pdf' onChange={handleChange}/>
<div>{(formik.errors.myFile) ? <p style={{color: 'red'}}>{formik.errors.myFile}</p> : null}</div>
<br/>
<button type='submit'>Submit</button>
</form>
)
}
export default MyForm;
That's it! You can check out the code output on Stackblitz here. Thank you for reading, and please let me know of your thoughts about this article.
Top comments (0)