by Bob MacNeal, 29 August 2019
In React’s brief history, one challenge has been how to share application state ― the ever-changing application data ― as needed within the component hierarchy of a typical React application. Sharing state falls under the auspices of state management.
React v16.8, released in February 2019, simplifies state management with the introduction of hooks. The useState hook, for component-level state management, and the useContext hook, for cross-component sharing and feature-level or application-level state management, are valuable additions to the React toolbox.
“Hooks let you use state and other React features without writing a class.”React v16.8: The One With Hooks
A wizard workflow is applied in react-wizard to a hypothetical task definition & scheduling feature (Figure 1).
An actual task definition and scheduling feature would require many more configuration steps and additional input types, but the pattern devised in react-wizard is conceived to be extensible.
All code in the walk-through can be viewed in detail in the react-wizard repository. Readers may also clone the repository, then follow the setup steps in the README.md to run the application locally.
The wizard feature is contained in Wizard.js. Wizard.js has a top level WizardProvider component.
WizardProvider represents the context wherein application state, named WizardContext, is stored. WizardProvider has three child components from top to bottom: StepIndicator, StepForm, and StepNavigator (Figure 2)
At the top of the view, the StepIndicator component is a visual indicator that communicates the current step to the user (Figure 3).
Below the StepIndicator is the StepForm component. The StepForm component has all of the form elements needed for user input (Figure 4).
The StepForm component is capable of rendering a dynamic number of form elements based on the content of the steps described by the initial state set in the WizardContext (more on WizardContext later).
Finally, beneath the StepForm component, is the StepNavigator component (Figure 5). The StepNavigator component has two buttons to navigate to the next or previous step.
The WizardProvider and its WizardContext enable the sharing of a state object within the StepIndicator, StepForm, and StepNavigator components.
When a user clicks the Next button on Step #1, a click handler in StepNavigator changes the active step in the state object. The StepIndicator component then responds to the active step state change by rendering itself as Step #2 (Figure 6).
State Updates via useContext
At each workflow step, StepForm.js (Figure 7) handles storing the user’s input via the handleChange function (line 42). The handleChange function accesses the state object via the useContext (WizardContext) hook (line 20).
WizardProvider, and its application state representation WizardContext (line 3, Figure 8), provide a state object for subsequent state updates as the user progresses through the steps. The state object is given an initial state with input values set to empty string. The initial state provides the name/value armature for each step.
The initial state includes definitions for each step represented by a 4-step steps array (line 8). This array is extensible to N-steps following a similar pattern.
Each element of the steps array indicates which form elements are needed at each step so that when the activeStep (line 7) changes, each step knows what input elements to render (text input, a checkbox, or a select) in StepForm.
Note that the first step in the steps array below calls for an element of type text (line 17).
Save Form Input
When the user progresses to the last step, the Next button is disabled and a Save button appears (Figure 9).
Clicking Save outputs a JSON object to the console of the form shown in Figure 10.
It’s left to readers to add form validation, wire up a POST to a backend data store, and to extend the steps array to N-steps with all required task configuration input.
Prior to React hooks, state was shared either by prop drilling or by wiring up a state container like react-redux. Both approaches have one or more of the drawbacks of tediousness, complexity, or excessive boilerplate.
The useState hook (for component-level state management) and the useContext hook (for feature/application state management) have significantly simplified state management in the typical React application.