You're reading from React Key Concepts
Introduction
State is one of the core concepts you must understand (and work with) to use React effectively. Basically, every React app utilizes (many) state values across many components to present a dynamic, reactive user interface.
From simple state values that contain a changing counter or values entered by users, all the way up to more complex state values such as the combination of multiple form inputs or user authentication information, state is everywhere. And in React apps, it's typically managed with the help of the useState()
Hook.
However, once you start building more complex React applications (e.g., online shops, admin dashboards, and similar sites), it is likely that you'll face various challenges related to state. State values might be used in component A but changed in component B or be made up of multiple dynamic values that may change for a broad variety of reasons (e.g., a cart in an online shop, which is a combination of products, where every product...
A Problem with Cross-Component State
You don't even need to build a highly sophisticated React app to encounter a common problem: state that spans multiple components.
For example, you might be building a news app where users can bookmark certain articles. The user interface could look like this:
As you can see in the preceding figure, the list of articles is on the left, and a summary of the bookmarked articles can be found in a sidebar on the right.
A common solution is to split this user interface into multiple components. The list of articles, specifically, would probably be in its own component—just like the bookmark summary sidebar.
However, in that scenario, both components would need to access the same shared state—that is, the list of bookmarked articles. The article list component would require access in order to add (or remove) articles. The bookmark summary sidebar component would...
Using Context to Handle Multi-Component State
React's context feature is one that allows you to create a value that can easily be shared across as many components as needed, without using props.
Using the context API is a multi-step process, the steps for which are described here:
- You must create a context value that should be shared.
- The context must be provided in a parent component of the components that need access to the context object.
- Components that need access (for reading or writing) must subscribe to the context.
React manages the context value (and its changes) internally and automatically distributes it to all components that have subscribed to the context.
Before any component may subscribe, however, the first step is to create a context object. This is done via React's createContext()
function:
import { createContext } from 'react'; createContext('Hello Context'); // a context with an initial string value...
Limitations of useState()
Thus far in this chapter, the complexity of cross-component state has been explored. But state management can also get challenging in scenarios where some state is only used inside a single component.
useState()
is a great tool for state management in most scenarios (of course, right now, it's also the only tool that's been covered). Therefore, useState()
should be your default choice for managing state. But useState()
can reach its limits if you need to derive a new state value that's based on the value of another state variable, as in this example:
setIsLoading(fetchedPosts ? false : true);
This short snippet is taken from a component where an HTTP request is sent to fetch some blog posts.
Note
You'll find the complete example code on GitHub at https://packt.link/FiOCM. You will also see more excerpts from the code later in this chapter.
When initiating the request, an isLoading
state value (responsible for showing...
Managing State with useReducer()
Just like useState()
, useReducer()
is a React Hook. And just like useState()
, it is a Hook that can trigger component function re-evaluations. But, of course, it works slightly differently; otherwise, it would be a redundant Hook.
useReducer()
is a Hook meant to be used for managing complex state objects. You will rarely (probably never) use it to manage simple string or number values.
This Hook takes two main arguments:
- A reducer function
- An initial state value
This brings up an important question: what is a reducer function?
Understanding Reducer Functions
In the context of useReducer()
, a reducer function is a function that itself receives two parameters:
- The current state value
- An action that was dispatched
Besides receiving arguments, a reducer function must also return a value: the new state. It's called a reducer function because it reduces the old state (combined with an action) to a new...
Summary and Key Takeaways
- State management can have its challenges—especially when dealing with cross-component (or app-wide) state or complex state values.
- Cross-component state can be managed by lifting state up or by using React's Context API.
- The Context API is typically preferable if you do a lot of prop drilling (forwarding state values via props across multiple component layers).
- When using the context API, you use
createContext()
to create a new context object. - The created context object yields a
Provider
component that must be wrapped around the part of the component tree that should get access to the context. - Components can access the context value via the
useContext()
Hook. - For managing complex state values,
useReducer()
can be a good alternative touseState()
. useReducer()
utilizes a reducer function that converts the current state and a dispatched action to a new state value.useReducer()
returns an array with exactly...
Apply What You Learned
Apply your knowledge about the context API and the useReducer()
Hook to some real problems.
Activity 10.1: Migrating an App to the Context API
In this activity, your task is to improve an existing React project. Currently, the app is built without the context API and so cross-component state is managed by lifting the state up. In this project, prop drilling is the consequence in some components. Therefore, the goal is to adjust the app such that the context API is used for cross-component state management.
Note
You can find the starting code for this activity at https://packt.link/93LSa. When downloading this code, you'll always download the entire repository. Make sure to then navigate to the subfolder with the starting code (activities/practice-1/starting-code
in this case) to use the right code snapshot.
The provided project also uses many features covered in earlier chapters. Take your time to analyze it and understand the provided code...