import React, { Fragment, useEffect, useState, cloneElement, FunctionComponentElement, } from 'react'; import './Form.scss'; import { Button } from './Button'; interface Props { inputs?: any[]; onSubmit?: (event: any) => void; setParentData?: (data: any) => void; submitButtonClassName?: string; submitButtonValue?: string; showSubmitButton?: boolean; parentSubmitted?: any; className?: string; parentId?: any; width?: string; id?: any; handleSubmit?: (event: any) => void; //all of these below come from cloneElement in renderInputs key?: any; key_?: any; state?: any; setInputToInitialize?: any; inputToInitialize?: any; setState?: any; submitted?: any; form?: any; } interface InputState { name?: string; value?: string; error?: string; } interface InputStateValueAndError { value?: string; error?: string; } interface FormattedInputStates { [name: string]: InputStateValueAndError; } export const Form: React.FC = ({ id, width, inputs, parentId, className = '', parentSubmitted, onSubmit = () => {}, submitButtonClassName, setParentData = () => {}, showSubmitButton = false, submitButtonValue = 'SUBMIT', }): JSX.Element => { const [inputToInitialize, setInputToInitialize] = useState(-1); // prevents race condition const [inputStates, setInputStates] = useState({}); const [submitted, setSubmitted] = useState(false); const getFormattedData = (data: InputState[]): FormattedInputStates => { const formattedData: FormattedInputStates = {}; Object.values(data).map((state: InputState) => { const name = state['name']; const value = state['value']; const error = state['error']; const valueAndError: InputStateValueAndError = {}; valueAndError['value'] = value; valueAndError['error'] = error; formattedData[name] = valueAndError; }); return formattedData; }; const formIsValid = (): boolean => { const errors = Object.values(inputStates).map( (state: InputState) => state['error'], ); return errors.every(error => error === ''); }; const initializeInputStates = (): void => { const newStates: InputState[] = []; inputs.forEach((_input: any, index: number) => { const state: InputState = {}; state['name'] = ''; state['value'] = ''; state['error'] = ''; newStates[index] = state; }); setInputStates(newStates); }; useEffect(() => { initializeInputStates(); setInputToInitialize(_ => 0); setParentData(getFormattedData(inputStates)); }, []); useEffect(() => { if (parentSubmitted) setSubmitted(true); }, [parentSubmitted]); useEffect(() => { setParentData(getFormattedData(inputStates)); }, [inputToInitialize]); const handleSubmit = (event: React.FormEvent) => { event.preventDefault(); setSubmitted(_ => true); const data = getFormattedData(inputStates); if (formIsValid()) { onSubmit(data); } }; const setState = (index: number, state: any) => { const newStates: any = Object.assign({}, inputStates); newStates[index] = state; setParentData(getFormattedData(newStates)); setInputStates(newStates); }; const submitButton = showSubmitButton ? ( ) : ( ); const renderInputs = (): Array> => inputs.map((input: JSX.Element, index: number) => cloneElement( input, { key: index, key_: index, state: inputStates[index], setInputToInitialize: setInputToInitialize, inputToInitialize: inputToInitialize, setState: setState.bind(this, index), submitted: submitted, form: parentId, }, [], ), ); return (
{renderInputs()} {submitButton}
); };