How to use the useReducer hook for simple form validation in React

Photo by davisuko on Unsplash

How to use the useReducer hook for simple form validation in React

The React useReducer has always been renowned for its ability to handle complex state management as opposed to the useState hook. Quite similar to the popular Redux Library, it is React’s own inbuilt state manger meant to handle problems like prop drilling through different components and it helps with performance also.

The Basics of the useReducer hook

The logic behind the useReducer hook is quite simple. Based on the JavaScript “reduce” function, the useReducer simply takes in two arguments; the reducer function and an initial state in that order

const [state, dispatch] = useReducer(reducer, initialState);

It then returns an array containing a state and a dispatch function

A reducer function is one that accepts two parameters; a state and an action. The reducer is called or executed by the dispatch function. The below is an example code of a reducer function for a simple counter

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return {count: state.count + 1};
    case 'decrement':
      return {count: state.count - 1};
    default:
      throw new Error();
  }
}

The dispatch function (which is returned from the useReducer hook) points or specifies the type of action that should executed by the reducer function.

dispatch({ type: 'increment' })

The action in the dispatch method can be expressed as an object with type and payload key. Payload is the information that will be added to the state by the action while type represents the identifier of the dispatched information.

How to use apply the useReducer hook for form validation

When building react forms without any React form validation library, it is necessary to track some states; the input value, if the input box has been touched by the user, if there is an error and a reset button.

We would need to apply some basic CSS to help in the validation

Now let’s look at a simple React form that accepts an email and password

import { useReducer } from 'react';

const initialValue = {
  email: '',
  password: '',
};

const reducer = (state, action) => {
  switch (action.type) {
    case 'email':
      return { ...state, email: action.payload };
    case 'password':
      return { ...state, password: action.payload };
    default:
      throw new Error(`Unknown action type: ${action.type}`);
  }
};

const Form = () => {
  const [state, dispatch] = useReducer(reducer, initialValue);
  return (
    <div>
      <input
        value={state.email}
        onChange={(event) =>
          dispatch({ type: 'email', payload: event.target.value })
        }
      />
      <input
        value={state.password}
        onChange={(event) =>
          dispatch({ type: 'password', payload: event.target.value })
        }
      />
    </div>
  );
};

export default Form;

If we want to take this further and be more specific, we would have a simple form box with a neat validation like this.

import { useReducer } from 'react';

const initialValue = {
  value: '',
  touched: false,
  error: null,
};

const reducer = (state, { type, payload }) => {
  switch (type) {
    case 'update':
      return {
        value: payload.value,
        touched: true,
        error: payload.error,
      };
    case 'reset':
      return initialValue;
    default:
      throw new Error(`Unknown action type: ${type}`);
  }
};

const Form = () => {
  const [state, dispatch] = useReducer(reducer, initialValue);

  return (
    <div>
      <input
        className={state.error ? 'error' : ''}
        value={state.value}
        onChange={(event) =>
          dispatch({
            type: 'update',
            payload: {
              value: event.target.value,
              error: state.touched ? event.target.value.length === 0 : null,
            },
          })
        }
      />
      <button onClick={() => dispatch({ type: 'reset' })}>reset</button>
    </div>
  );
};

export default Form;

Then you can finalize by adding your CSS styles here.

error {
  border-color: red;
}

.error:focus {
  outline-color: red;
}

There you have it! A simple way to add the useReducer hook. This can be be massively improved to add different various inputs as demanded.