// TODO: Enable exportDefaultFrom in Babel syntax import React, { useReducer } from 'react' import styles from './styles.module.css' export const Input = require('./input.js').default export const Button = require('./button.js').default export const FormController = function FormController({errors, errorDispatch, children, onSubmit}){ const initialState = { fields: {} } const reducer = (state, action)=>{ let fields = {...state.fields} const prevField = state.fields[action.name] // If no value (onBlur), just mark touched and clear error if(action.value === undefined){ errorDispatch({type: 'clear_error', field: action.name}) return { fields: { ...fields, [action.name]: { ...prevField, touched: true, isValid: prevField.validate?prevField.validate(prevField.value, fields):true } } } } // Update currently changing field const updatedField = { ...prevField, value: action.value !== undefined ? action.value : prevField.value, isValid: prevField.validate?prevField.validate(action.value, fields):true } fields[action.name] = updatedField // Update other fields where the validate function takes whole state const fieldNames = Object.keys(fields) for(const name of fieldNames){ if(name === action.name) continue const field = fields[name] if(field.validate && field.validate.length > 1) fields[name] = { ...field, isValid: field.validate(field.value, fields) } } return {fields} } // Update initial state React.Children.forEach(children, child => { if(!child.props.name) return; initialState.fields[child.props.name] = { name: child.props.name, validate: child.props.validate, value: child.props.initialValue || "", isValid: false, touched: false } }) // Create reducer const [state, dispatch] = useReducer(reducer, initialState) const submitForm = ev=>{ if(ev) ev.preventDefault(); const fields = {} for(const name in state.fields){ const field = state.fields[name] fields[field.name] = field.value } onSubmit(fields) } const _children = React.Children.map(children, child => { if(child.type === Button && child.props.type.toLowerCase() === 'submit') return React.cloneElement(child, { enabled: !Object.values(state.fields).some(field=>!field.isValid), onClick: submitForm }) const {name} = child.props; if(!name) return child; return React.cloneElement(child, { onChange: ev=>dispatch({name, value: ev.target.value}), onBlur: ev=>dispatch({name}), value: state.fields[name].value, isValid: state.fields[name].touched ? state.fields[name].isValid : true, error: errors ? errors[child.props.name] : undefined }) }) return (
) }