How to Build a Form in React with useReducer

dtroyano86

Daniel Troyano

Posted on July 19, 2020

How to Build a Form in React with useReducer

Oh what's that? You are starting to understand useReducer and want to see it in a practical example?

Ok! Let's employ useReducer to build a form in React!


First let's make a functional component in React to hold our form.

import React, {useReducer} from "react";

const form = () => {
  return (
    <div>
      <form>
        //Our form fields are going to go between those form tags
      </form>
    </div>
    );
};

export default form;

We're also going to make an initial form object. It's going to contain several objects that each represent a different form field. Each one will have a label, the title displayed to the user, and a value.

const initialForm = {
  username: {
    label: 'Username',
    value: '',
  },
  email: {
    label: 'Email',
    value: '',
  },
  password: {
    label: 'Password',
    value: '',
  },
};

Before we go too much farther we should also set up an Input component to represent each form field and import that into our form so we can use it there.

Our input component is going to be a functional component, which takes all the elements in one form object, a change handler, and an id. The id is their key in the original object.

It's going to just return an input tag wrapped with a label that shows the label we want the user to see.

import React from 'react';

const input = ({id, changed, value, label}) => (
  <label>{label}
    <input id={id} onChange={changed} value={value} />
  </label>
);

export default input;

Ok, now that we have those basic pieces set up, let's talk about how to implement useReducer here.

We're going to call it with the initial form values from up above and a very simple reducer that will just assume all we want to change is the value on the given object.

function formReducer (prevState, {value, key}) {
  const updatedElement = {...prevState[key]};
  updatedElement.value = value;
  return {...prevState, [key]: updatedElement};
};

const [state, dispatch] = useReducer(formReducer, initialForm);

Notice the use of a computed property name in the return statement, thanks ES6!

Now all we have to do is dynamically build our form in our original component.

<form>
  {Object.keys(state).map(key => (
    <Input
      changed={({target: {value}}) => dispatch({value, key})}
      key={key}
      id={key}
      value={state[key].value}
      label={state[key].label}
    />
  ))}
</form>

We're getting all the keys in our state object, which is our form, and creating a new array of Input components based on those keys. Each Input component will take a key and an id, which are the same, and are the key in the original object. They will also take a value and a label. In addition they will take a callback function that calls dispatch with the current value and the key of the object.

And that's it! A simple form using useReducer. But we can go one step deeper. Who wants to use callback functions? Not me! Let's make our own hook to handle the state of the form so that we don't have to pass down callback functions anymore! Check out how to do that in my next article!

💖 💪 🙅 🚩
dtroyano86
Daniel Troyano

Posted on July 19, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related