const { useState, useEffect } = React;

// THE HOOK
// + required args: initialValues
// + optional args: validationSchema, handleSubmit
// suggest importing from a separate file

const useForm = ({ initialValues, validationSchema, handleSubmit }) => {
  const [values, setValues] = useState(initialValues || {});
  const [touched, setTouched] = useState({});
  const [errors, setErrors] = useState({});
  const [valid, setValid] = useState(false);
  const formKeys = Object.keys(values);

  useEffect(() => {
    handleErrors();
    handleTouched();
  }, [values]);

  const handleErrors = () => {
    setValid(true);
    if (!validationSchema) {
      return;
    }
    const errorValues = formKeys.reduce((obj, key) => {
      const { match, error } = validationSchema[key];
      const valid = match.test(values[key]);
      if (!valid) {
        obj[key] = error;
        setValid(false);
      }
      return obj;
    }, {});
    setErrors(errorValues);
  };

  const handleTouched = () => {
    const touchedValues = formKeys.reduce((obj, key) => {
      if (touched[key] || values[key].length) {
        obj[key] = true;
      }
      return obj;
    }, {});
    setTouched(touchedValues);
  };

  const onChange = (e) => {
    const id = e.target.name;
    setValues({ ...values, [id]: e.target.value });
  };

  const onSubmit = () => {
    handleSubmit(values);
  };

  return {
    values,
    errors,
    touched,
    valid,
    onChange,
    onSubmit
  };
};

export default useForm;
View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://unpkg.com/react@16.7.0-alpha.0/umd/react.development.js
  2. https://unpkg.com/react-dom@16.7.0-alpha.0/umd/react-dom.development.js