How to use React State - Robin Wieruch

How to use React State - Robin Wieruch

State in React is one of the most important topics when learning React . State breathes life into your React application. It’s what makes your application grow beyond static content being displayed on a website, because a user can interact with it. Every interaction of a user with your application may change the underlying state which lead to changes in the UI represented by the state.

In this extensive walkthrough, I want to guide you through all the possibilities of state management in React. We will start with simple state management that is co-located to React components, exploring all its features with React Hooks, and end with more complex global state in React managed by React itself or third-party libraries.

Once you went through this state management in React walkthrough, you should have a good grasp of what’s state in React. Maybe it goes beyond this and you get a good idea about how state should be managed in an ideal scenario in your next React application.

The UI of a frontend application is a representation of its state. State is just a snapshot in time. If a user changes state by interacting with your application, the UI may look completely different afterward, because it’s represented by this new state rather than the old state.

State can be various things:

State is just another fancy word for a JavaScript data structure representing the state with JavaScript primitives and objects. For instance, a simple state could be a JavaScript boolean whereas a more complex UI state could be a JavaScript object:

Each of these states could be managed by a single React component which is mainly doing three things:

This can be done within a React component with React Hooks . I am saying within here, because it’s co-located state to the React component by using Hooks. Later you will learn about other state that is managed globally and outside of React components. Let’s explore the React Hooks for state first.

React’s useState hook is for many React beginners their first encounter with state in React:

The useState hook takes an initial state as argument, just for the first time the React component renders, and returns an array with two values: the current state and the state update function. Whereas the current state is used to display it somewhere within your React component, the state update function is used to change the current state (e.g. HTML button onClick ).

Taking it one step further, it cannot be just used to increase an integer, but also to capture more dynamic state of an input HTML element when typing into it. Because the input HTML element takes the current state as value, it becomes a controlled component/element. Not the internal HTML manages the state anymore, but React’s state management:

After all, React’s useState is your gateway into state management with React. Everything that follows from here is more powerful yet more complex.

React’s useReducer derives from the concept of a JavaScript Reducer . The idea: A reducer function takes the current state and an action with payload and computes it to a new state:

A reducer function may look like the following for managing the state of a list of todo items and their complete status:

Depending on the incoming action’s type, one of the switch cases is taken to either complete or incomplete a todo item. The action’s payload, here the id property, tells the reducer which todo item in the list, which is the state itself, should be toggled. All the other todo items are not changed.

Now consider the following initial state for a React component:

A React component using this reducer function with React’s useReducer hook may look like the following:

In this scenario, there is only an initial list of todo items where an individual item can be toggled to completed or in-completed. The useReducer hook takes the previously defined reducer and an initial state as arguments, just for the first time the React component renders, and returns an array with two values: the current state and the state update function.

In contrast to the React’s useState hook, the state update function from the useReducer hook always takes an action as argument. It’s commonly called dispatch because it “dispatches” an action to the reducer. Each action comes with a mandatory type property, which is used later in the reducer function, and additional payload, which is here the todo item’s id property.

When to use useReducer or useState? Generally speaking, React’s useReducer hook can be used over React’s useState hook, if (A) a state transition becomes more complex, (B) the state object isn’t a simple JavaScript primitive anymore, or most importantly, if (C) multiple states are conditionally related to each other. Naturally this happens if a React application grows beyond a small-sized project.

Technically React’s useContext hook isn’t related to state. It makes it just more convenient to pass props down the component tree. Normally React props are passed from parent to child components ; however, React’s Context API allows it to tunnel React components in between. Thus it’s possible to pass props from a grandfather component to a grandchild component without bothering the other React components in between of the chain.

However, React’s Context API mechanism is indeed used for more advanced state management in React: It tunnels the current state and the state update function — either returned from useState or useReducer — through many React components. This way, developers started to manage React state at top-level components with useReducer/useState and pass their returned values — current state and state update function — with useContext to all the React child components that are interested in this state or updating this state.

In the previous code snippet, the ability to modify todo items with the dispatchTodos updater function is made globally available with React’s Context API. In another step, React’s useContext hook can be used in any child component to retrieve the dispatch function. Follow along in the exercises of this section to explore this concept in detail.

When to combine useContext with useReducer or useState?

These are the three main strategies for state management in React:

All three strategies map to the following types of state:

In addition, enabling all three strategies map to various features or combinations of these features within or outside of React’s capabilities:

You are not limited to just one of these strategies. Whereas smaller applications start out with managing state in a React component with useState and useReducer hooks, in a growing application developers start to manage state globally too, for state that is needed by more than one React component and state that is needed to be shared among a multitude of React components.

Technically React’s useReducer + useContext hooks from strategy (2) enable one to create their own state management library like Redux from strategy (3).

Let’s explore such implementation together with useCombinedReducers in the exercises of this section.

Even though React’s useReducer came into the world after Redux, its concept origins from Redux itself. Redux just takes state management on another level. One may speak of the state is truly managed globally by an external force outside of React.

Whereas Action => Reducer(s) => Store encapsulates Redux. Let’s recap all parts of Redux briefly in JS. This is a Redux Reducer that acts on two Redux Actions which has no dependencies on the Redux library at all:

The Redux store which knows about the Redux Reducer:

Then, the Redux Store offers a small API surface to interact with it — e.g. dispatching a Redux Action:

Finally, in JavaScript, you can listen to changes with the Redux Store:

That’s Redux in a nutshell with all its fragments: Action, Reducer, Store. If you attach the store subscription to React, the React UI can update whenever the state in Redux changes.

Another popular alternative for Redux is MobX for state in React: Both state management libraries got very popular in the early days of React. However, there are other state management libraries out there, competing with both titans, by offering a more lightweight state management solution.

What makes all kinds of state the same is the nature of its transitions from one state to another state. However, the origin of state differs for frontend applications. State can origin within the client application (frontend) or from a remote server application (backend).

For instance, state that origins within the client application can be a boolean flag of for the status of an open/closed dialog component. The client application defines the initial state (e.g. closed dialog) and defines the state transitions + the actual possible states (e.g. boolean flag is set to false or true):

In contrast, if state origins from a remote server application, the initial state and the transitions may be defined in the client application — e.g. the initial state is null but once data arrives from an API the state is set to the actual data — but the possible state coming from the backend application isn’t foreseeable for the client application.

Why do we need to know about this at all? Managing state that origins within the client application tends to be easier to manage than managing state coming from a backend application. The former, managing state that origins from the client application, can be achieved with all three strategies we have learned about:

The latter, managing state that origins from the server application, tends to be more complex. It doesn’t only come with no data (e.g. null ) or actual filled data states, but also with additional states for error and progress. In addition, it’s a repetitive process to set up all these states with your chosen strategy and it’s a real pain once you consider advanced topics like caching and stale state. It comes with lots of pain points.

That’s where another technology comes into play: GraphQL.

GraphQL is not strictly related to state. GraphQL is an alternative to REST for client-server communication . However, with the right GraphQL library in place for your React application, managing state that origins from a server application becomes much simpler.

For instance, Apollo Client is one of these GraphQL client libraries. It can be used to read and write data from and to a remote GraphQL API via GraphQL queries and mutations. For instance, using a query to read data with Apollo within a React component may look the following way:

Even though GraphQL is just used to define the GraphQL query, the Query component from Apollo Client makes sure to give you all the states necessary to represent the whole data fetching process in the UI. In this case, it gives you data and a loading state, but you can also access error state and more. There is no need to write all the state transitions yourself, you just leave it to the Apollo Client GraphQL library.

Also caching is taken care of in advanced GraphQL Client library. There are multiple advanced features which help you to avoid stale data and avoid unnecessary data fetching procedures, because the data is already there and cached for you.

Now, knowing about state that origins in client and server applications, it may be the best solution to differentiate between both origins by splitting up the responsibilities the following way:

client origin state management solutions

server origin state management solutions

For many React applications, I strongly believe it would make state management a breeze if just GraphQL and a powerful GraphQL client library would be used to accommodate the server originated state. What’s left is the UI state which can be easily managed by React’s Hooks. There is even no strong need for Redux anymore.

If you are not using React Class Components but only React Function Components , you don’t need to read any further here. If you are still using React Class Components, then either

The following example shows you how to manage state in React Class Components:

Either way, you can manage state in Class Components and Function Components. However, only React Hooks in React Function Components enable you to use more modern and powerful state management solutions in React. Combining React Hooks with GraphQL may be the ultimate combination for taming the state in React.

Recommended articles