Using useContext and useReducer to Manage a Form’s State
By: Rajat Kumar
Tags: React, Programming, Reactjs, JavaScript, React Hook

Building the Form

We’ll build a simple form to collect a user’s information. It is broken down into several components and each component can be as complex as needed. But for now, it is pretty simple:

The initial state of the form looks something like this:

Using Context

You might have noticed that we are not passing any state props to the different form components. We’ll use React’s Context API to pass our state around. We don’t have the props-drilling problem in this small example, but it can easily creep up when the form grows and you need to pass the state via props down to the children of the several form components.

So let’s create a Context to store this state:

This file exports three things:

FormContext — A context object initialised with null . This will ultimately be used to hold the state.

— A context object initialised with . This will ultimately be used to hold the state. FormProvider — This is a React component that comes with every Context object. This component accepts a prop called value that stores any given value. Any child component of this provider has access to the stored value.

— This is a React component that comes with every Context object. This component accepts a prop called that stores any given value. Any child component of this provider has access to the stored value. useFormContext — A custom Hook to get the value stored in our context.

Using Reducer

Next, we will create a Reducer that will contain the logic to modify the state:

The useImmerReducer Hook takes two arguments: the Reducer itself and the initial state value. It returns the state and a function to update this state (here, we are calling it dispatch ). In simpler terms, this function can be thought of as the conventional setState method.

A Reducer ( FormReducer ) accepts two arguments:

state — Every time the Reducer is called, it receives the most recent version of the state.

— Every time the Reducer is called, it receives the most recent version of the state. action — This can be any info that is used to modify the state. Usually, it as an object that looks like this:

{

key: 'aKeyFromStateObject',

payload: {}, // a new value for state[key]

}

Handling events

Coming back to the form, most forms have a handleEvent or handleChange function to handle the various input/select/checkbox/etc. events. Our function looks something like this:

This can be integrated with an input like this:

<input

value={state.name.value}

onChange={(e) => handleChange("name", e.target.value)}

/>

Using Provider

So far, we have created the initial state, created the Reducer, and attached the event handler to our inputs. Now let’s provide the state values to the form components using the FormProvider component:

Here, we are passing the state and the handleChange function to the value prop of the FormProvider . These can be accessed in any of the child form components.

Using our custom Hook to get value stored in Context

Remember the useFormContext custom Hook we created to retrieve the Context values? Let’s see how it can be used: