Complete Guide to state management in React part 1

Sunscrapers Team

24 August 2022, 7 min read

thumbnail post

What's inside

  1. Intro
  2. Redux
  3. Redux Toolkit
  4. Context
  5. Recoil

Intro

Let us start with the question - Is state management necessary in React?

State management allows developers to determine the application’s state to ensure the changes reflect real-world context and business processes. It is one of any dynamic application’s most essential and unavoidable features. It is simply keeping track of how your application data changes over time.

It is a problem as old as React itself.

However, managing state in React apps is not as simple as it might seem. Not only because there are plenty of different kinds of states, but also because often there are infinite ways of managing each kind.

Many methods of managing a state include class-based state management and third-party libraries like Redux.

It is important to know what states you need to manage in React apps:

  1. Local state
  2. Global state
  3. Server state
  4. URL state

React provides a simple and flexible API to support state management in a React component. The local component state management got much easier thanks to the useState hook.

Hooks allow developers to write more readable code that is much easier to share and maintain. This method of state management does not require the use of classes.

The difference between Hooks and class-based state management is that there are no objects that hold the state. You can break up the state into multiple pieces and update it independently.

Global state management requires using a third-party solution - tried and tested libraries, but new libraries are also coming into the mix.

We would like to walk you through the most important - according to us - tools:

  1. Redux
  2. Redux Toolkit
  3. Context
  4. Recoil
  5. Jotai
  6. XState
  7. Mobx
  8. Zustand
  9. React Query

Redux

Redux is one of the oldest and most widely used global state management libraries.

Redux is a library inspired by Flux and can be considered an implementation of Flux. Both use very popular patterns. The main difference between these two is that Flux provides multiple Stores per application, while Redux provides a single Store per app. Redux keeps everything in one region of the application.

It can be used as a data store for any UI layer. The most common usage is with React and React Native, but bindings are available for Angular, Angular 2, Vue, Mithril, and more. Redux provides a subscription mechanism that any other code can use.

Redux provides a pattern for managing the application state. If you do not face a problem with state management, the benefits of Redux might be hard for you to understand.

The stage where Redux is the most useful combines declarative view implementation that interferes with the UI updates from state changes like React.

Following the tips from the Redux website, if you are a new learner, you should focus on learning React first and wait to learn Redux until you are already familiar and comfortable with React.

There is a recommendation to use a library evolved from Redux called Redux Toolkit, which simplifies how to use Redux.

Redux Toolkit

Redux Toolkit was created to address three of the most common concerns about Redux:

  • “Configuring a Redux store is too complicated”
  • “I have to add many packages to get Redux to do anything useful”
  • “Redux requires too much boilerplate code”

Redux Toolkit is dedicated to all Redux users. It does not matter how experienced or high your skill level is. It simply makes writing Redux applications easier and speeds up the development process.

Remember that you are not required to use Redux Toolkit to use Redux. However, it is strongly recommended to use the Redux Toolkit for all Redux apps.

There are many middlewares for Redux e.g., redux-thunk redux-saga, and redux-observable.

Redux Toolkit relies on redux-thunk. Before you had to create actions, reducers and states separately. Now you can create slices in one file.

Also, you can make the slices load only on the pages where the state is needed. Which means that you don’t have to keep a big state object in memory, you can just load it when you want to.

import { createSlice, PayloadAction } from '@reduxjs/toolkit' 

interface CounterState { 
  value: number 
} 

const initialState = { value: 0 } as CounterState 

const counterSlice = createSlice({ 
  name: 'counter', 
  initialState, 
  reducers: { 
    increment(state) { 
      state.value++ 
    }, 
    decrement(state) { 
      state.value-- 
    }, 
    incrementByAmount(state, action: PayloadAction<number>) { 
      state.value += action.payload 
    },
    }, 
}) 

export const { increment, decrement, incrementByAmount } = counterSlice.actions 
export default counterSlice.reducer 

And then you can use exported actions as functions in your component.

There is more in the code sandbox provided by redux-toolkit documentation. It shows the circular dependencies problem and how to use redux-toolkit to some extent.

Also - Redux - has one of the best dev tools from all of the state management libraries.

Redux dev tools support time travel, so you can see how the application changes live, stop it and rewind - Redux Starter Kit: Circular Slice Dependencies Example.

Redux should be used when you expect to receive your state from multiple sources and you need to make some calculations based on those sources.

Context

Context is React API that easily allows sharing states across the entire or part of the app. Thanks to Context API, you can pass props from parent to child components at different levels without needing to use props-drilling or pass the props on every level. It is also touted as an easier approach to state management using Redux.

It is often used with some libraries for Global State management like React-Query.

While using Context, it comes to a consumer and a provider. You need to specify context provider and consumer to use it. React documentation provides a simple example.

However, there are some cons of using Context.

Everything that uses data from Context re-renders every time that Context’s state is changed.

If you use Context in many places in your application, it can cause a lot of re-renders. Some optimization tricks can help avoid it, but if you are a less experienced developer, then it might be the thing that you should also consider.

Generally speaking, Context is not designed for data that changes often.

const ThemeContext = React.createContext('light'); 

class App extends React.Component { 
  render() { 
    // Use a Provider to pass the current theme to the tree below. 
    // Any component can read it, no matter how deep it is.
    // In this example, we pass "dark" as the current value. 
    return ( 
      <ThemeContext.Provider value="dark"> 
        <Toolbar /> 
      </ThemeContext.Provider> 
    ); 
  } 
} 

// A component in the middle doesn't have to 
// pass the theme down explicitly anymore. 
function Toolbar() { 
  return ( 
    <div> 
      <ThemedButton /> 
    </div> 
  ); 
} 

class ThemedButton extends React.Component { 
  // Assign a contextType to read the current theme context. 
  // React will find the closest theme Provider above and use its value. 
  // In this example, the current theme is "dark". 
  static contextType = ThemeContext; 
  render() { 
    return <Button theme={this.context} />; 
  } 
} 

Recoil

Recoil is an open-source stage management library invented by a Software Engineer at Facebook, and maintained by them (also they are maintaining React). It is rather big library - 14kb size eg. Jotai is only 3,3kb (only - compared to Recoil).

It provides a global state which means that all components in a React application can share states easily and it is minimal especially when compared to Redux with no boilerplate code setup needed.

Recoil is pretty easy to use. It lets you create a data-flow graph that flows from atoms through selectors and down into your React components.

First you set up an atom, a piece of your state you want to re-use in multiple places.

const todoListState = atom({ 
  key: 'TodoList', 
  default: [], 
}); 

And then you can use it in similar way as useState hook:

const [todoList, setTodoList] = useRecoilState(todoListState);

Recoil also has developer tools and uses snapshots, but the API is not stable yet and might change in the future.

Read the second part of our article, where we covered Jotai, XState, Mobx, Zustand, and React Query.

Sunscrapers Team

Sunscrapers empowers visionary leaders to ride the wave of the digital transformation with solutions that generate tangible business results. Thanks to agile and lean startup methods, we deliver high-quality software at top speed and efficiency.

Tags

react
state management
redux
redux toolkit
context
recoil

Share

Let's talk

Discover how software, data, and AI can accelerate your growth. Let's discuss your goals and find the best solutions to help you achieve them.

Hi there, we use cookies to provide you with an amazing experience on our site. If you continue without changing the settings, we’ll assume that you’re happy to receive all cookies on Sunscrapers website. You can change your cookie settings at any time.