import React, { useState, RefObject } from 'react'; import get from 'lodash.get'; import set from 'lodash.set'; import { FieldValidator } from './types'; const initialContext: { formRef: RefObject; formName: string; setFormName: (name: string) => void; submitCount: number; incrementSubmitCount: () => void; isSubmitting: boolean; setIsSubmitting: (submitting: boolean) => void; submitButtonName?: string; setSubmitButtonName: (name?: string) => void; fieldNameToErrorId: Map; setError: (fieldName: string, errorId: string) => void; removeError: (fieldName: string) => void; hasValidation: boolean; fieldValidators: Map | FieldValidator[]>; validate: (values: any) => object; } = { formRef: { current: null }, formName: '', setFormName: (name: string) => {}, submitCount: 0, incrementSubmitCount: () => {}, isSubmitting: false, setIsSubmitting: () => {}, submitButtonName: undefined, setSubmitButtonName: () => {}, fieldNameToErrorId: new Map(), setError: (fieldName: string, errorId: string) => {}, removeError: (fieldName: string) => {}, hasValidation: false, fieldValidators: new Map(), validate: () => ({}) }; const FormContext = React.createContext(initialContext); type Props = { formRef: RefObject; children: React.ReactNode; }; export function FormContextProvider({ formRef, children }: Props) { const [formName, setFormName] = useState(''); // submitCount is for redux-form. Formik maintains its own submitCount const [submitCount, setSubmitCount] = useState(0); const [isSubmitting, setIsSubmitting] = useState(false); const [submitButtonName, setSubmitButtonName] = useState(); const [fieldNameToErrorId, setFieldNameToErrorId] = useState< Map >(new Map()); const [fieldValidators] = useState( new Map React.ReactNode | undefined>() ); const validate = (values: any) => { const errors = {}; [...fieldValidators.entries()].forEach(([key, validator]) => { const value = get(values, key); let error: React.ReactNode; if (Array.isArray(validator)) { for (let validate of validator) { error = validate(value); if (error) break; } } else { error = validator(value); } error && set(errors, key, error); }); return errors; }; const context = { formRef, formName, setFormName, submitCount, incrementSubmitCount: () => setSubmitCount(submitCount + 1), isSubmitting, setIsSubmitting, submitButtonName, setSubmitButtonName, fieldNameToErrorId, fieldValidators, hasValidation: false, validate, setError: (fieldName: string, errorId: string) => { const map = new Map( fieldNameToErrorId.set(fieldName, errorId) ); setFieldNameToErrorId(map); }, removeError: (fieldName: string) => { if (fieldNameToErrorId.has(fieldName)) { fieldNameToErrorId.delete(fieldName); const map = new Map(fieldNameToErrorId); setFieldNameToErrorId(map); } } }; return ( {children} ); } export default FormContext;