В данном видео вы узнаете как работать с файлами в Formik и как их валидировать в yup. Так же вы познакомитесь со способами изменения схемы валидации поля, в зависимости от данных других полей.
import React from 'react'; import { Formik, FieldArray } from 'formik' import * as yup from 'yup' import './App.scss' function App() { const getError = (touched, error) => { return touched && error && <p key={error} className={'error'}>{error}</p> } const validationsSchema = yup.object().shape({ addressRegister: yup.string().required(), likeRegister: yup.bool(), addressActual: yup.string().when('likeRegister', { is: false, then: yup.string().required() }), file: yup.array().of(yup.object().shape({ file: yup.mixed().test('fileSize', 'Размер файла больше 10 байт', (value) => { if (!value) return false return value.size < 10 }).required(), type: yup.string().oneOf([`application/vnd.ms-publisher`], 'Добавьте файл с правильным форматов').required(), name: yup.string().required() }).typeError('Добавьте файл')).required() }) const getFileSchema = (file) => (file && { file: file, type: file.type, name: file.name }) const getArrErrorsMessages = (errors) => { const result = [] errors && Array.isArray(errors) && errors.forEach((value) => { if (typeof value === 'string') { result.push(value) } else { Object.values(value).forEach((error) => { result.push(error) }) } }) return result } return ( <div> <Formik initialValues={{ addressRegister: '', likeRegister: false, addressActual: '', file: undefined }} validateOnBlur onSubmit={(values) => { console.log(values) }} validationSchema={validationsSchema} > {({ values, errors, touched, handleChange, handleBlur, isValid, handleSubmit, dirty }) => ( <div className={`from`}> <p> <label htmlFor={`addressRegister`}>Адрес регистрации</label><br /> <input className={'input'} type={`text`} name={`addressRegister`} onChange={handleChange} onBlur={handleBlur} value={values.addressRegister} /> </p> {getError(touched.addressRegister, errors.addressRegister)} <p> <label htmlFor={`likeRegister`}>Адреса совпадают <input type={`checkbox`} name={`likeRegister`} onChange={handleChange} onBlur={handleBlur} checked={values.likeRegister} /> </label> </p> {!values.likeRegister && <p> <label htmlFor={`addressActual`}>Адрес проживания</label><br /> <input className={'input'} type={`text`} name={`addressActual`} onChange={handleChange} onBlur={handleBlur} value={values.addressActual} /> </p> } {!values.likeRegister && getError(touched.addressActual, errors.addressActual)} {console.log('file', values.file)} {console.log('fileErrors', errors.file)} <FieldArray name={`file`}> {(arrayHelper) => ( <> <p> <input type={`file`} name={`file`} onChange={(event) => { const { files } = event.target const file = getFileSchema(files.item(0)) if (!file) { arrayHelper.remove(0) } if (Array.isArray(values.file)) { arrayHelper.replace(0, file) } else { arrayHelper.push(file) } }} /> </p> {getArrErrorsMessages(errors.file).map((error) => getError(true, error))} </> )} </FieldArray> <button disabled={!isValid || !dirty} onClick={handleSubmit} type={`submit`} >Отправить</button> </div> )} </Formik> </div> ); } export default App;
Рассматривается две темы:
Для изменения схемы валидации в yup есть метод when. Он доступен для всех типов схем yup (string, number, array, mixed, …). Метод when принимает 2 аргумента: ключ поля или массив ключей полей, значения которых влияют на схемы валидации; «билдер», состоящий (в основном) из двух полей «is» и «then». Поле «is» должно содержать значение или функцию. Если оно содержит значение, то нужно вставить такое значение поля, на которое мы ссылаемся, которое нам нужно, чтобы применить новую схему. Если это функция, то она просто должна вернуть true. Аргументами в ней будут значения из формы, ключи которых мы перечислим в первом аргументе метода when . Поле «then» — это схема, по которой будет валидироваться поле в случае если «is» вернёт true.
const validationsSchema = yup.object().shape({ addressRegister: yup.string().required(), likeRegister: yup.bool(), addressActual: yup.string().when('likeRegister', { is: false, then: yup.string().required() })})
Выше приведён код, который читается следующим образом: есть поле «likeRegister», которое обозначает, что фактический адрес совпадает с юридическим. Если адреса совпадают, то поле «addressActual» не обязательно, а если же не совпадают (likeRegister === false), то поле становится обязательным.
Я уже рассматривал как можно работать с файлами в Formik и озвучил несколько решений проблем, которые есть в этой связке в статье «Создание FileList. Передаём FileList в input ref React«. Там есть подробный код с созданием компонента с файловым инпутом.
Для понимания этого кода нужно уяснить пару вещей:
const avaliableMimeType = ['application/pdf', 'image/jpeg', 'image/png'] // Внутри схемы type: yup.string().oneOf(avaliableMimeType, 'Недопустимый тип файла')
В данном примере мы разрешили принимать pdf, jpeg/jpg и png. Больше mime-type вы можете найти в интернете, например в википедии