State management refers to the process of managing and manipulating the state of a React application. State is the data or variables that determine the behavior of a component, and can include things like form values, user input, and application data.
In React, state is typically managed at the component level, with each component maintaining its own state. This can be sufficient for simple applications with small or medium-sized state trees, but as the complexity of the application increases, it can become challenging to manage state in this way.
To address this issue, developers often turn to state management libraries, which provide tools and techniques for managing state in a more predictable and consistent way. These libraries allow you to store and manipulate state in a central location, rather than having to pass it down the component tree through props. This can make it easier to update and query state, and can help to prevent the need for complex props drilling.
Here are the 10 most popular state management libraries for React:
#1 Redux
Redux is a widely-used state management library that allows you to store all of your applicationβs state in a single store, and provides tools for updating and querying the state in a predictable and consistent way. It is especially useful for applications with complex state trees or that require the ability to undo and redo actions.
To use Redux in a React application, first, install the redux
and react-redux
libraries:
1npm install redux react-redux
Next, create a reducer function that specifies how the state should be updated in response to actions:
1const counterReducer = (state = 0, action) => {2 switch (action.type) {3 case 'INCREMENT':4 return state + 1;5 case 'DECREMENT':6 return state - 1;7 default:8 return state;9 }10};
Then, create a store using the reducer function:
1import { createStore } from 'redux';23const store = createStore(counterReducer);
Now, you can use the store to update and query the state of your application. For example, you can dispatch actions to the store to update the state:
1store.dispatch({ type: 'INCREMENT' });
You can also use the storeβs getState
method to retrieve the current state:
1console.log(store.getState()); // Output: 1
Finally, you can use the react-redux
library to connect your React components to the store, allowing them to access and update the state:
1import { connect } from 'react-redux';23const mapStateToProps = (state) => ({4 count: state,5});67const mapDispatchToProps = (dispatch) => ({8 increment: () => dispatch({ type: 'INCREMENT' }),9 decrement: () => dispatch({ type: 'DECREMENT' }),10});1112const Counter = ({ count, increment, decrement }) => (13 <div>14 <p>Count: {count}</p>15 <button onClick={increment}>Increment</button>16 <button onClick={decrement}>Decrement</button>17 </div>18);1920export default Counter;
Visit Redux.js.org for the documentation π
#2 MobX
MobX is based on the concept of reactive programming. It allows you to easily manage and manipulate the state of your application by using observable data structures and reactive computations. MobX is known for its simplicity and ease of use, and is a good choice for developers who are new to state management.
Install the mobx and mobx-react libraries:
1npm install mobx mobx-react
Next, create a store class that holds the state of your application. This store should contain any variables that you want to be able to update and observe:
1import { observable } from 'mobx';23class CounterStore {4 @observable count = 0;5}67const store = new CounterStore();
Now, you can use the store to update and query the state of your application. For example, you can define methods on the store to update the state:
1class CounterStore {2 @observable count = 0;34 increment() {5 this.count++;6 }78 decrement() {9 this.count--;10 }11}1213const store = new CounterStore();
You can also use the store to update and query the state of your application. For example, you can call the increment and decrement methods on the store to update the state:
1store.increment();2store.decrement();
Visit MobX.js.org for more details π
#3 React Context
React Context is a built-in feature of React that allows you to pass data down the component tree without the need for props drilling. It is a simple and lightweight alternative to libraries like Redux or MobX, and is particularly well-suited for applications with small or medium-sized state trees.
To use React Context in a React application, you will need to create a context using the createContext
function, and then provide the context to your components using the Context.Provider
component. Here is an example of how you might use React Context to manage the state of a simple counter application:
First, create a context using the createContext
function:
1import { createContext } from 'react';23const CounterContext = createContext();
Next, create a provider component that wraps your application and provides the context to your components:
1import { useState } from 'react';23const CounterProvider = ({ children }) => {4 const [count, setCount] = useState(0);56 const increment = () => setCount(count + 1);7 const decrement = () => setCount(count - 1);89 return (10 <CounterContext.Provider value={{ count, increment, decrement }}>11 {children}12 </CounterContext.Provider>13 );14};
Now, you can use the CounterContext.Consumer
component to access the context within your components:
1import { useContext } from 'react';23const Counter = () => {4 const { count, increment, decrement } = useContext(CounterContext);56 return (7 <div>8 <p>Count: {count}</p>9 <button onClick={increment}>Increment</button>10 <button onClick={decrement}>Decrement</button>11 </div>12 );13};
Finally, wrap your application in the CounterProvider
component:
1import { render } from 'react-dom';23render(4 <CounterProvider>5 <App />6 </CounterProvider>,7 document.getElementById('root')8);
This will make the context available to all of the components within the CounterProvider
, allowing them to access and update the state using the useContext
hook.
Read more about the Context API from the React.js website π
#4 Unstated
Unstated is a lightweight state management library for React that allows you to manage the state of your application using simple container components. It is based on the concept of dependency injection, and uses a declarative approach to state management by creating container components that hold and manipulate the state, and then injecting the state into your presentational components as props. Unstated is known for its simplicity and minimalistic approach to state management.
Install the unstated library:
1npm install unstated
Create a container component that manages the state of your application:
1import { Container } from 'unstated';23class CounterContainer extends Container {4 state = {5 count: 0,6 };78 increment() {9 this.setState({ count: this.state.count + 1 });10 }1112 decrement() {13 this.setState({ count: this.state.count - 1 });14 }15}1617const counter = new CounterContainer();
Now, you can use the container to update and query the state of your application. For example, you can call the increment
and decrement
methods on the container to update the state:
1counter.increment();2counter.decrement();
You can also access the state
property of the container to retrieve the current state:
1console.log(counter.state.count); // Output: 0
Finally, you can use the inject
higher-order component provided by Unstated to inject the container into your components, allowing them to access and update the state:
1import { inject } from 'unstated';23const Counter = ({ counter }) => (4 <div>5 <p>Count: {counter.state.count}</p>6 <button onClick={counter.increment}>Increment</button>7 <button onClick={counter.decrement}>Decrement</button>8 </div>9);1011export default inject(counter)(Counter);
Check out the unstated repository in GitHub π
#5 Apollo Client
Apollo Client is a popular state management library for managing GraphQL data in React applications. It provides tools for querying and mutating GraphQL data, and allows you to easily integrate your application with a GraphQL server. Apollo Client is a good choice for developers who are using GraphQL in their applications.
Here is an example of using Apollo Client for state management in a React application:
1import React from 'react';2import { useQuery, useMutation } from '@apollo/client';34import { GET_TODO, TOGGLE_TODO } from '../queries';56function Todo({ id }) {7 const { data, loading, error } = useQuery(GET_TODO, { variables: { id } });8 const [toggleTodo] = useMutation(TOGGLE_TODO);910 if (loading) return <p>Loading...</p>;11 if (error) return <p>Error: {error.message}</p>;1213 const { todo } = data;1415 return (16 <div>17 <input18 type="checkbox"19 checked={todo.completed}20 onChange={() => toggleTodo({ variables: { id } })}21 />22 <label>{todo.text}</label>23 </div>24 );25}
In this example, the useQuery
hook is used to fetch a todo item with a specific ID from the server, and the useMutation
hook is used to toggle the completion status of the todo item. The TOGGLE_TODO
mutation updates the server and the local cache automatically with the new data.
The GET_TODO
and TOGGLE_TODO
queries and mutations should be defined separately. Hereβs an example of how they might look:
1import { gql } from '@apollo/client';23export const GET_TODO = gql`4 query GetTodo($id: ID!) {5 todo(id: $id) {6 id7 text8 completed9 }10 }11`;1213export const TOGGLE_TODO = gql`14 mutation ToggleTodo($id: ID!) {15 toggleTodo(id: $id) {16 id17 completed18 }19 }20`;
Read the Apollo Client documentation from the Apollo GraphQL website π
#6 Recoil
Recoil is a new state management library from Facebook that is designed for building large-scale applications with complex state trees. It allows you to manage your applicationβs state using a graph-based model, and provides tools for efficiently querying and updating the state.
1import React from 'react';2import { atom, useRecoilState } from 'recoil';34const todoListState = atom({5 key: 'todoListState',6 default: [],7});89function TodoList() {10 const [todoList, setTodoList] = useRecoilState(todoListState);1112 const addTodo = (newTodo) => {13 setTodoList((oldTodoList) => [...oldTodoList, newTodo]);14 };1516 return (17 <div>18 {todoList.map((todo) => (19 <Todo key={todo.id} todo={todo} />20 ))}21 <AddTodoForm onAddTodo={addTodo} />22 </div>23 );24}2526function AddTodoForm({ onAddTodo }) {27 const [text, setText] = React.useState('');2829 const handleSubmit = (event) => {30 event.preventDefault();31 onAddTodo({ text, completed: false });32 setText('');33 };3435 return (36 <form onSubmit={handleSubmit}>37 <input38 type="text"39 value={text}40 onChange={(event) => setText(event.target.value)}41 />42 <button type="submit">Add Todo</button>43 </form>44 );45}4647function Todo({ todo }) {48 return <div>{todo.text}</div>;49}
In this example, the todoListState
atom is defined and initialized with an empty array. The useRecoilState
hook is used to read and update the value of the todoListState
atom. The TodoList
component displays a list of todo items and a form for adding new todo items. The AddTodoForm
component has a form with an input field and a submit button. When the form is submitted, a new todo item is added to the list by calling the onAddTodo
callback prop with the new todo item.
Visit the Recoil website for more information π
#7 Zustand
Zustand is a lightweight state management library that is based on the concept of hooks. It allows you to manage the state of your application using simple functions that are called inside your components, and is particularly well-suited for applications with small or medium-sized state trees.
1import React from 'react';2import create from 'zustand';34const useStore = create((set) => ({5 todoList: [],6 addTodo: (newTodo) => set((state) => ({7 todoList: [...state.todoList, newTodo],8 })),9}));1011function TodoList() {12 const { todoList, addTodo } = useStore();1314 return (15 <div>16 {todoList.map((todo) => (17 <Todo key={todo.id} todo={todo} />18 ))}19 <AddTodoForm onAddTodo={addTodo} />20 </div>21 );22}2324function AddTodoForm({ onAddTodo }) {25 const [text, setText] = React.useState('');2627 const handleSubmit = (event) => {28 event.preventDefault();29 onAddTodo({ text, completed: false });30 setText('');31 };3233 return (34 <form onSubmit={handleSubmit}>35 <input36 type="text"37 value={text}38 onChange={(event) => setText(event.target.value)}39 />40 <button type="submit">Add Todo</button>41 </form>42 );43}4445function Todo({ todo }) {46 return <div>{todo.text}</div>;47}
In this example, the useStore
hook is used to create a store with a todoList
state variable and an addTodo
action. The TodoList
component displays a list of todo items and a form for adding new todo items. The AddTodoForm
component has a form with an input field and a submit button. When the form is submitted, a new todo item is added to the list by calling the onAddTodo
callback prop with the new todo item.
Check out the Zustand repository in GitHub π
#8 XState
XState is based on the concept of finite state machines. It allows you to model the different states and transitions of your application using a simple and expressive syntax, and provides tools for updating and querying the state in a predictable and consistent way.
1import React from 'react';2import { useMachine } from '@xstate/react';3import { Machine } from 'xstate';45const todoMachine = Machine({6 id: 'todo',7 initial: 'idle',8 states: {9 idle: {10 on: {11 ADD: 'adding',12 },13 },14 adding: {15 on: {16 SUBMIT: 'idle',17 CANCEL: 'idle',18 },19 },20 },21});2223function TodoList() {24 const [current, send] = useMachine(todoMachine);25 const { todoList } = current.context;2627 return (28 <div>29 {current.matches('idle') && (30 <button onClick={() => send('ADD')}>Add Todo</button>31 )}32 {current.matches('adding') && (33 <AddTodoForm34 onSubmit={(newTodo) => {35 send('SUBMIT', { newTodo });36 }}37 onCancel={() => send('CANCEL')}38 />39 )}40 {todoList.map((todo) => (41 <Todo key={todo.id} todo={todo} />42 ))}43 </div>44 );45}4647function AddTodoForm({ onSubmit, onCancel }) {48 const [text, setText] = React.useState('');4950 const handleSubmit = (event) => {51 event.preventDefault();52 onSubmit({ text, completed: false });53 setText('');54 };5556 return (57 <form onSubmit={handleSubmit}>58 <input59 type="text"60 value={text}61 onChange={(event) => setText(event.target.value)}62 />63 <button type="submit">Add Todo</button>64 <button type="button" onClick={onCancel}>Cancel</button>65 </form>66 );67}6869function Todo({ todo }) {70 return <div>{todo.text}</div>;71}
In this example, the todoMachine
is a finite state machine (FSM) that manages the state of the todo list application. The useMachine
hook is used to interpret the FSM and provide the current state and an action dispatcher to the component. The TodoList
component displays a button for adding a new todo item or a form for adding a new todo item, depending on the current state. When the form is submitted, the new todo item is added to the list by dispatching the SUBMIT
action.
Read further on the XState.js.org documentation π
#9 Easy Peasy
Easy Peasy is based on the concept of a global store and an abstraction of Redux. It allows you to manage the state of your application using a single, centralized store, and provides tools for updating and querying the state in a predictable and consistent way. Easy Peasy is known for its simplicity and ease of use as tagged with being developer-friendly.
1import React from 'react';2import { useStoreState, useStoreActions } from 'easy-peasy';34const store = {5 todos: {6 items: [],7 add: (state, payload) => {8 state.items.push(payload);9 },10 },11};1213function TodoList() {14 const todoList = useStoreState((state) => state.todos.items);15 const addTodo = useStoreActions((actions) => actions.todos.add);1617 return (18 <div>19 {todoList.map((todo) => (20 <Todo key={todo.id} todo={todo} />21 ))}22 <AddTodoForm onAddTodo={addTodo} />23 </div>24 );25}2627function AddTodoForm({ onAddTodo }) {28 const [text, setText] = React.useState('');2930 const handleSubmit = (event) => {31 event.preventDefault();32 onAddTodo({ text, completed: false });33 setText('');34 };3536 return (37 <form onSubmit={handleSubmit}>38 <input39 type="text"40 value={text}41 onChange={(event) => setText(event.target.value)}42 />43 <button type="submit">Add Todo</button>44 </form>45 );46}4748function Todo({ todo }) {49 return <div>{todo.text}</div>;50}
In this example, the store is defined with a todos
property that has an array of todo items
and an add
action. The useStoreState
hook is used to read the value of the items state variable, and the useStoreActions
hook is used to get the add action. The TodoList
component displays a list of todo items and a form for adding new todo items. The AddTodoForm
component has a form with an input field and a submit button. When the form is submitted, a new todo item is added to the list by calling the onAddTodo
callback prop with the new todo item.
Check out the Easy Peasy repository in GitHub π
#10 Valtio
Valtio is a state management library for React that uses the Observer pattern to synchronize state changes between components. It provides a simple API for managing state, including creating stores, reading and updating state values, and subscribing to state changes.
In Valtio, a store is an object that holds the applicationβs state variables and actions. State variables are values that represent the state of the application, and actions are functions that can update the state variables. Components can use the useStore
hook to read the values of state variables and actions from the store and subscribe to updates. When a state variable or action is updated, Valtio will automatically re-render the subscribed components with the new values.
1import React from 'react';2import { createStore, useStore } from 'valtio';34const todoStore = createStore({5 todos: [],6 addTodo: (newTodo) => {7 todoStore.todos.push(newTodo);8 },9});1011function TodoList() {12 const { todos, addTodo } = useStore(todoStore);1314 return (15 <div>16 {todos.map((todo) => (17 <Todo key={todo.id} todo={todo} />18 ))}19 <AddTodoForm onAddTodo={addTodo} />20 </div>21 );22}2324function AddTodoForm({ onAddTodo }) {25 const [text, setText] = React.useState('');2627 const handleSubmit = (event) => {28 event.preventDefault();29 onAddTodo({ text, completed: false });30 setText('');31 };3233 return (34 <form onSubmit={handleSubmit}>35 <input36 type="text"37 value={text}38 onChange={(event) => setText(event.target.value)}39 />40 <button type="submit">Add Todo</button>41 </form>42 );43}4445function Todo({ todo }) {46 return <div>{todo.text}</div>;47}
In this example, the todoStore
is a store object with a todos
state variable and an addTodo
action. The useStore
hook is used to read the value of the todos state
variable and the addTodo
action from the store. The TodoList
component displays a list of todo items and a form for adding new todo items. The AddTodoForm
component has a form with an input field and a submit button. When the form is submitted, a new todo item is added to the list by calling the onAddTodo
callback prop with the new todo item.
The useSnapshot
hook in Valtio is used to get a snapshot of the current state of a store. It returns an object with the values of all the state variables in the store at the time the hook is called.
1import { createStore, useSnapshot } from 'valtio';23const todoStore = createStore({4 todos: [],5 addTodo: (newTodo) => {6 todoStore.todos.push(newTodo);7 },8});910function TodoList() {11 const { todos, addTodo } = useStore(todoStore);12 const snapshot = useSnapshot(todoStore);1314 console.log(snapshot); // { todos: [ { text: 'Take out the trash', completed: false }, ... ] }1516 return (17 <div>18 {todos.map((todo) => (19 <Todo key={todo.id} todo={todo} />20 ))}21 <AddTodoForm onAddTodo={addTodo} />22 </div>23 );24}2526function AddTodoForm({ onAddTodo }) {27 const [text, setText] = React.useState('');2829 const handleSubmit = (event) => {30 event.preventDefault();31 onAddTodo({ text, completed: false });32 setText('');33 };3435 return (36 <form onSubmit={handleSubmit}>37 <input38 type="text"39 value={text}40 onChange={(event) => setText(event.target.value)}41 />42 <button type="submit">Add Todo</button>43 </form>44 );45}4647function Todo({ todo }) {48 return <div>{todo.text}</div>;49}
The useSnapshot
hook is called with the todoStore
as an argument. It returns an object with the values of all the state variables in the store, which can be logged to the console or used in other ways.
Take a glimpse at the Valtio repository in GitHub π
There are many other state management libraries available for React, and the best one for your application will depend on your specific needs and requirements. Itβs important to carefully evaluate the different options and choose the one that best fits your needs.
Feel free to mention some worthy ππ» state management libraries you can vouch for on the comments section below. ππ»