Skip to content

React custom hook for form state

Posted on:August 31, 2021 at 06:25 PM

Lets say we have following form:

const UserSignup = () => {
  const [userName, setUserName] = useState('');
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [passwordConfirmation, setPasswordConfirmation] = useState('');
  const [investmentInterest, setInvestmentInterest] = useState(false);

  const handleSubmit = event => {
    event.preventDefault();
    clear();
  };

  const clear = () => {
    setUserName('');
    setEmail('');
    setPassword('');
    setPasswordConfirmation('');
    setInvestmentInterest(false);
  };

  return (
    <form className="UserSignup" onSubmit={handleSubmit}>
      <label htmlFor="userName">User Name</label>
      <input
        id="userName"
        name="userName"
        type="text"
        value={userName}
        required
        onChange={event => {
          setUserName(event.target.value);
        }}
      />
      <label htmlFor="email">Email Address</label>
      <input
        id="email"
        name="email"
        type="email"
        value={email}
        required
        onChange={event => {
          setEmail(event.target.value);
        }}
      />
      <label htmlFor="password">Password</label>
      <input
        id="password"
        name="password"
        type="password"
        value={password}
        required
        onChange={event => {
          setPassword(event.target.value);
        }}
      />
      <label htmlFor="passwordConfirmation">Confirm Password</label>
      <input
        id="passwordConfirmation"
        name="passwordConfirmation"
        type="password"
        value={passwordConfirmation}
        required
        onChange={event => {
          setPasswordConfirmation(event.target.value);
        }}
      />
      <label htmlFor="investmentInterest" className="UserSignup--checkbox">
        <input
          id="investmentInterest"
          name="investmentInterest"
          type="checkbox"
          checked={investmentInterest}
          onChange={event => setInvestmentInterest(event.target.checked)}
        />
        Do you want to maybe help us out with an angel investment?
      </label>
      <input type="Submit" />
    </form>
  );
};

export default UserSignup;

We are using multiple setState to mantain state of the form fields can we simplify this? yes we can :).

Lets create a new file named useSetState.js with following contents:


import { useReducer } from 'react';

const reducer = (previousState = {}, updatedState = {}) => {
  return { ...previousState, ...updatedState };
};

const useSetState = (initialState = {}) => {
  const [state, dispatch] = useReducer(reducer, initialState);

  const setState = updatedState => dispatch(updatedState);

  return [state, setState];
};

export default useSetState;

We are creating a reducer function which takes two objects and merges them together, if there are common fields the updateState overwrites the previousState.

Then we are creating a hook name useSetState in recieves initialState which by default is empty object. We use the react’s provided useReducer hook and pass a reducer to it which we created in the last step.

We create a function named setState this function calls the dispatch of useReducer with the updatedState as a param.

Now we can update our UserSignup as follow:

import React, { useState } from 'react';
import useSetState from './useSetState';

import './UserSignup.css';

const initialState = {
  userName: '',
  email: '',
  password: '',
  passwordConfirmation: ''
};

const UserSignup = () => {
  const [state, setState] = useSetState(initialState);

  const handleChange = event => {
    setState({
      [event.target.name]: event.target.value
    });
  };

  const handleSubmit = event => {
    event.preventDefault();
    clear();
  };

  const clear = () => {
    setState(initialState);
  };

  return (
    <form className="UserSignup" onSubmit={handleSubmit}>
      <label htmlFor="userName">User Name</label>
      <input
        id="userName"
        name="userName"
        type="text"
        value={state.userName}
        required
        onChange={handleChange}
      />
      <label htmlFor="email">Email Address</label>
      <input
        id="email"
        name="email"
        type="email"
        value={state.email}
        required
        onChange={handleChange}
      />
      <label htmlFor="password">Password</label>
      <input
        id="password"
        name="password"
        type="password"
        value={state.password}
        required
        onChange={handleChange}
      />
      <label htmlFor="passwordConfirmation">Confirm Password</label>
      <input
        id="passwordConfirmation"
        name="passwordConfirmation"
        type="password"
        value={state.passwordConfirmation}
        required
        onChange={handleChange}
      />
      <input type="Submit" />
    </form>
  );
};

export default UserSignup;

This simplified and easy to read then the previous approach. This article was inspired from here