Back

Top Five Lightweight State Management Libraries for React

Top Five Lightweight State Management Libraries for React

State management libraries provide an organized manner to handle and update your application’s current data or condition (state). They let you keep track of the state of your application in a consolidated and structured manner, making it simpler to manage, update, and maintain its state. There are many options for React, and this article will focus on the top five lightweight libraries.

This article will examine and compare five distinct lightweight libraries for React to determine the optimal choice for your next React application.

1. React Hook Form

React Hook Form is a lightweight and performant framework for handling form states in React. It takes advantage of React hooks to give a straightforward API for constructing reusable forms with little boilerplate code. Using React Hook Form, you can quickly manage form validation, error messages, and submit logic. It’s an excellent solution for creating sophisticated forms in React apps.

-

import React from "react";
import { useForm } from "react-hook-form";

function contactForm() {
  const {
    register,
    handleSubmit,
    formState: { errors },
  } = useForm();

  const onSubmit = (data) => {
    console.log(data);
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <label htmlFor="name">Name:</label>
      <input type="text" id="name" {...register("name", { required: true })} />
      {errors.name && <span className="error">This field is required</span>}

      <label htmlFor="email">Email:</label>
      <input
        type="email"
        id="email"
        {...register("email", { required: true, pattern: /^\S+@\S+$/i })}
      />
      {errors.email?.type === "required" && (
        <span className="error">This field is required</span>
      )}
      {errors.email?.type === "pattern" && (
        <span className="error">Invalid email address</span>
      )}

      <label htmlFor="message">Message:</label>
      <textarea id="message" {...register("message", { required: true })} />
      {errors.message && <span className="error">This field is required</span>}

      <button type="submit">Submit</button>
    </form>
  );
}

export default contactForm;

2. Zustand

Zustand library is a small, fast, and scalable bare-bones state management solution using the simplified black principle, and it has grown in popularity among React developers as a lightweight and efficient way of handling state in their apps. “Zustand” is a German word for “state”.

It contains hook API, so it is very easy to consume in React applications. It supports Async methods and easily integrates additional middleware like immer, dev tools, etc. Easily integrated with other state management libraries like Redux & React Context API, states can be accessed outside of the React components.

-

First, create a store. Your store is a hook! You make a new copy of the data to change something instead of changing the original. The set function makes updating easier by merging changes with existing data.

import create from 'zustand'

// create a store to hold our application's state
const useAppStore = create((set) => ({
  loggedInUser: null,
  notifications: [],
  setLoggedInUser: (user) => set({ loggedInUser: user }),
  addNotification: (notification) =>
    set((state) => ({ notifications: [...state.notifications, notification] })),
  clearNotifications: () => set({ notifications: [] }),
}));

Then bind your components. Use the hook anywhere; no providers are needed. Select your state, and the component will re-render on changes.

// a component that displays the logged in user's name
function userDisplay() {
  const loggedInUser = useAppStore((state) => state.loggedInUser);
  return <h1>Welcome, {loggedInUser.name}!</h1>;
}

// a component that allows the user to log out
function logOutButton() {
  const setLoggedInUser = useAppStore((state) => state.setLoggedInUser);
  return <button onClick={() => setLoggedInUser(null)}>Logout</button>;
}

3. Recoil

Recoil is a state management library for React applications that helps you manage and share states in a more efficient and predictable way. It was released to the public by a team at Facebook in May 2020 as an alternative to Redux and offers a simpler, more flexible approach to state management.

Atoms and Selectors are the two main concepts for Recoil.

Atoms are discrete units of state that store pieces of state in react.

A selector is a derived state that passes the current state to a pure function, which then computes a new value based on that state. In other words, a selector is a state representation derived from a specific computation performed on the original state.

-

const boxState = atom({
    key: "box",
    default: {color: #000000}
});

You can create, export, and use an atom anywhere in your app. And you use it in the same way that you would use a useState. To use atoms, you will need to utilize a Recoil hook, such as useRecoilState

export const Box = ({ id }: { id: number }) => {
  const [box, setBox] = useRecoilState(boxState(id));

  return (
    <div
      className="box"
      onClick={() => {
        setBox({ color: randomColor() });
      }}
      style={{
        backgroundColor: box.color,
      }}
    />
  );
};

Recoil can be used to generate a new state from an atom using selectors. Simply retrieve the values of the atoms that you require in your selector, and whenever the values of those atoms change, the selector will rerun, and the components that use it will re-render and display the new value.

const userInfoState = selector({
  key: "userInfo",
  get: async ({ get }) => {
    //asynchronous state
    const userId = get(UserIdState);

    const info = await API.userInfo(userId);
    return info;
  },
});

To use selectors, you will need to utilize a Recoil hook, such as useRecoilValue.

const userInfo = () => {
  const userInfo = useRecoilValue(userInfoState);
};

Session Replay for Developers

Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — an open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.

4. React Context API

React Context API is a built-in React library that allows you to share data across components without having to manually send it down the component tree via props. This can make your code more readable and cleaner. You may establish a context with a value, and any component that requires that value can use the useContext hook to obtain it. The Context API may be used to store a variety of data kinds, including themes, user authentication, and even a shopping cart.

-

Assume we’re developing a website that lists a user’s top movies. We want to store the movie list and make it available to all components that use it. Using the Context API, we can build a movieContext to hold the list of movies.

First, we’ll create a new file called movieContext.js. In this file, we’ll import the createContext function from React and use it to create our context:

import { createContext } from 'react';

export const movieContext = createContext();

Next, We’ll use a movieProvider component with the React Context API to manage the movie list. We’ll store the list using useState and fetch it using useEffect. This allows us to access the list across the app without passing it down through the component tree:

import { useState, useEffect } from "react";

export const movieProvider = ({ children }) => {
  const [movies, setMovies] = useState([]);

  useEffect(() => {
    fetch("https://api.example.com/movies")
      .then((response) => response.json())
      .then((data) => setMovies(data));
  }, []);

  return (
    <movieContext.Provider value={movies}>{children}</movieContext.Provider>
  );
};

Lastly, we’ll encase our entire app in the movieProvider component to make the movie state accessible to all components:

import { movieProvider } from "./movieContext";

function App() {
  return (
    <movieProvider>
      <div className="App">
        <Header />
        <movieList />
      </div>
    </movieProvider>
  );
}

Any component that requires access to the list of movies may now use the useContext hook to retrieve the information from the movieContext:

import { useContext } from "react";
import { movieContext } from "./movieContext";

export const movieList = () => {
  const movies = useContext(movieContext);

  return (
    <ul>
      {movies.map((movie) => (
        <li key={movie.id}>{movie.title}</li>
      ))}
    </ul>
  );
};

5. Redux Toolkit

Think of Redux as a centralized brain for your application, where all the important decisions and actions are made. It keeps track of the current state of your application and makes it easy to update and access that state from anywhere in your code. It runs in different environments (client, server, and native).

Redux Toolkit is like having a personal assistant who takes care of all the small details for you. It provides you with pre-built functions and guidelines that simplify the process of writing Redux code, so you can focus on building your application’s features instead of worrying about the nitty-gritty details of Redux.

-

Assume you’re developing a shopping cart application and need to handle the status of the goods in the cart. You may use the Redux Toolkit to define a slice of the state that handles the cart items. Here’s some code to get you started:

import { createSlice } from "@reduxjs/toolkit";

const cartSlice = createSlice({
  name: "cart",
  initialState: [],
  reducers: {
    addItem: (state, action) => {
      state.push(action.payload);
    },
    removeItem: (state, action) => {
      const index = state.findIndex((item) => item.id === action.payload.id);
      if (index !== -1) {
        state.splice(index, 1);
      }
    },
  },
});

export const { addItem, removeItem } = cartSlice.actions;

export default cartSlice.reducer;

To use this cart slice in your Redux store, you would import it and add it to the combineReducers function:

import { configureStore } from "@reduxjs/toolkit";
import cartReducer from "./cartSlice";

const store = configureStore({
  reducer: {
    cart: cartReducer,
  },
});

export default store;

Now, to add an item to the cart, you can dispatch the addItem action:

import { useDispatch } from "react-redux";
import { addItem } from "./cartSlice";

function AddToCartButton({ item }) {
  const dispatch = useDispatch();

  const handleClick = () => {
    dispatch(addItem(item));
  };

  return <button onClick={handleClick}>Add to Cart</button>;
}

Finally, to display the items in the cart, you can use the useSelector hook to get the cart state from the Redux store:

import { useSelector } from "react-redux";

function Cart() {
  const cartItems = useSelector((state) => state.cart);

  return (
    <div>
      <h2>Cart</h2>
      <ul>
        {cartItems.map((item) => (
          <li key={item.id}>{item.name}</li>
        ))}
      </ul>
    </div>
  );
}

Conclusion

Every project is unique and has its own set of dependencies and business use cases, and fits with one solution over another. All of the methods listed above seek to handle the same problem in various ways.

  • React Hook Form: Suitable for constructing robust forms with features like automated form validation and error handling. Well-documented and beginner-friendly.

  • Zustand: A lightweight state management solution ideal for small to medium-sized apps that don’t require a full-featured solution like Redux.

  • Recoil: A more capable state management option designed for managing complex states in larger applications.

  • React Context API: Provides good performance for simple applications but may not scale well for larger or more complicated projects. Testing and measuring performance is crucial in determining its suitability.

  • Redux Toolkit: Offers speed improvements through features like memoized selectors and improved Redux store updates. Effective for handling state in React applications.

Regardless of the state management library you select, you’ll need to analyze your development team’s skills and overall comfort with React.

Gain Debugging Superpowers

Unleash the power of session replay to reproduce bugs and track user frustrations. Get complete visibility into your frontend with OpenReplay, the most advanced open-source session replay tool for developers.

OpenReplay