Top Related Projects
The official, opinionated, batteries-included toolset for efficient Redux development
🐻 Bear necessities for state management in React
Simple, scalable state management.
👻 Primitive and flexible state management for React
Quick Overview
use-immer is a React hook that simplifies state management by allowing you to mutate state directly using the Immer library. It provides an intuitive way to update complex nested state structures while maintaining immutability under the hood.
Pros
- Simplifies state updates for complex nested objects
- Reduces boilerplate code compared to traditional React state management
- Maintains immutability while allowing seemingly mutable operations
- Integrates seamlessly with React's useState hook
Cons
- Adds an additional dependency to your project
- May have a slight performance overhead for very simple state updates
- Learning curve for developers unfamiliar with Immer's concepts
- Potential overuse might lead to less explicit state management
Code Examples
- Basic usage with a simple object:
import { useImmer } from "use-immer";
function Counter() {
const [state, setState] = useImmer({ count: 0 });
const increment = () => {
setState(draft => {
draft.count += 1;
});
};
return <button onClick={increment}>{state.count}</button>;
}
- Updating nested state:
import { useImmer } from "use-immer";
function UserProfile() {
const [user, updateUser] = useImmer({
name: "John",
address: { city: "New York", country: "USA" }
});
const updateCity = newCity => {
updateUser(draft => {
draft.address.city = newCity;
});
};
return (
<div>
<p>{user.name} lives in {user.address.city}</p>
<button onClick={() => updateCity("Los Angeles")}>Move to LA</button>
</div>
);
}
- Working with arrays:
import { useImmer } from "use-immer";
function TodoList() {
const [todos, updateTodos] = useImmer([
{ id: 1, text: "Learn React", done: false },
{ id: 2, text: "Try use-immer", done: false }
]);
const toggleTodo = id => {
updateTodos(draft => {
const todo = draft.find(t => t.id === id);
todo.done = !todo.done;
});
};
return (
<ul>
{todos.map(todo => (
<li key={todo.id} onClick={() => toggleTodo(todo.id)}>
{todo.text} - {todo.done ? "Done" : "Not done"}
</li>
))}
</ul>
);
}
Getting Started
To start using use-immer in your React project:
-
Install the package:
npm install use-immer
-
Import and use the hook in your component:
import { useImmer } from "use-immer"; function MyComponent() { const [state, setState] = useImmer(initialState); // Use setState to update state const updateState = () => { setState(draft => { // Mutate draft directly }); }; return ( // Your JSX here ); }
Competitor Comparisons
The official, opinionated, batteries-included toolset for efficient Redux development
Pros of Redux Toolkit
- Provides a more comprehensive state management solution with built-in tools for async operations and middleware
- Includes utilities for creating and managing slices of state, reducing boilerplate code
- Offers better integration with Redux DevTools for debugging and time-travel debugging
Cons of Redux Toolkit
- Steeper learning curve due to its more complex architecture and concepts
- Requires more setup and configuration compared to use-immer's simplicity
- Can be overkill for smaller applications or simpler state management needs
Code Comparison
Redux Toolkit:
import { createSlice } from '@reduxjs/toolkit';
const counterSlice = createSlice({
name: 'counter',
initialState: 0,
reducers: {
increment: state => state + 1,
},
});
use-immer:
import { useImmer } from 'use-immer';
const [count, setCount] = useImmer(0);
const increment = () => setCount(draft => draft + 1);
Summary
Redux Toolkit offers a more robust state management solution with additional features and tools, making it suitable for larger applications with complex state requirements. However, it comes with a steeper learning curve and more setup overhead. use-immer, on the other hand, provides a simpler approach to state management, focusing on immutable updates with less boilerplate, making it ideal for smaller projects or components that require localized state management.
🐻 Bear necessities for state management in React
Pros of zustand
- Lightweight and minimalistic, with a smaller bundle size
- Supports middleware and devtools out of the box
- Can be used outside of React components
Cons of zustand
- Less intuitive for managing complex nested state
- Doesn't provide built-in immutability guarantees
- Requires more boilerplate for larger applications
Code Comparison
zustand:
import create from 'zustand'
const useStore = create(set => ({
count: 0,
increment: () => set(state => ({ count: state.count + 1 })),
}))
use-immer:
import { useImmer } from 'use-immer'
const [state, setState] = useImmer({ count: 0 })
const increment = () => setState(draft => { draft.count++ })
Key Differences
- zustand uses a single store approach, while use-immer is typically used with React's useState
- use-immer provides a more intuitive API for updating nested state
- zustand offers more flexibility in terms of where and how the store can be accessed
Use Cases
- zustand: Best for applications requiring global state management with minimal setup
- use-immer: Ideal for components with complex, nested state that benefit from immutable updates
Simple, scalable state management.
Pros of MobX
- More comprehensive state management solution with built-in reactivity
- Supports complex scenarios and large-scale applications
- Offers automatic tracking of observables and computed values
Cons of MobX
- Steeper learning curve due to its more complex API
- Requires more boilerplate code for setup and configuration
- Can lead to less predictable behavior in some cases
Code Comparison
MobX:
import { makeObservable, observable, action } from "mobx";
class TodoStore {
todos = [];
constructor() {
makeObservable(this, {
todos: observable,
addTodo: action
});
}
addTodo(text) {
this.todos.push({ text, completed: false });
}
}
use-immer:
import { useImmer } from "use-immer";
function TodoList() {
const [todos, updateTodos] = useImmer([]);
const addTodo = (text) => {
updateTodos((draft) => {
draft.push({ text, completed: false });
});
};
// ...
}
use-immer provides a simpler API for immutable state updates, making it easier to use and understand for smaller applications or components. MobX offers a more powerful and flexible solution for complex state management needs but requires more setup and knowledge to use effectively.
👻 Primitive and flexible state management for React
Pros of Jotai
- Lightweight and minimalistic, with a smaller bundle size
- Supports atomic state management, allowing for fine-grained updates
- Built-in support for async state and derived state
Cons of Jotai
- Steeper learning curve for developers familiar with Redux-like patterns
- Less established ecosystem compared to Immer
Code Comparison
use-immer:
import { useImmer } from "use-immer";
const [state, setState] = useImmer({ count: 0 });
setState(draft => { draft.count += 1 });
Jotai:
import { atom, useAtom } from "jotai";
const countAtom = atom(0);
const [count, setCount] = useAtom(countAtom);
setCount(prev => prev + 1);
Both libraries aim to simplify state management in React applications, but they take different approaches. use-immer focuses on providing immutable state updates using Immer's draft concept, while Jotai emphasizes atomic state management with a more flexible API. use-immer may be more intuitive for developers accustomed to working with immutable data structures, while Jotai offers a more granular approach to state management that can be beneficial for complex applications with interdependent state.
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual CopilotREADME
use-immer
A hook to use immer as a React hook to manipulate state.
Installation
npm install immer use-immer
API
useImmer
useImmer(initialState)
is very similar to useState
.
The function returns a tuple, the first value of the tuple is the current state, the second is the updater function,
which accepts an immer producer function or a value as argument.
Managing state with immer producer function
When passing a function to the updater, the draft
argument can be mutated freely, until the producer ends and the changes will be made immutable and become the next state.
Example: https://codesandbox.io/s/l97yrzw8ol
import React from "react";
import { useImmer } from "use-immer";
function App() {
const [person, updatePerson] = useImmer({
name: "Michel",
age: 33
});
function updateName(name) {
updatePerson(draft => {
draft.name = name;
});
}
function becomeOlder() {
updatePerson(draft => {
draft.age++;
});
}
return (
<div className="App">
<h1>
Hello {person.name} ({person.age})
</h1>
<input
onChange={e => {
updateName(e.target.value);
}}
value={person.name}
/>
<br />
<button onClick={becomeOlder}>Older</button>
</div>
);
}
(obviously, immer is a little overkill for this example)
Managing state as simple useState hook
When passing a value to the updater instead of a function, useImmer
hook behaves the same as useState hook and updates the state with that value.
import React from 'react';
import { useImmer } from 'use-immer';
function BirthDayCelebrator(){
const [age, setAge] = useImmer(20);
function birthDay(event){
setAge(age + 1);
alert(`Happy birthday #${age} Anon! hope you good`);
}
return(
<div>
<button onClick={birthDay}>It is my birthday</button>
</div>
);
}
Obviously if you have to deal with immutability it is better option passing a function to the updater instead of a direct value.
useImmerReducer
Immer powered reducer, based on useReducer
hook
Example: https://codesandbox.io/s/2zor1monvp
import React from "react";
import { useImmerReducer } from "use-immer";
const initialState = { count: 0 };
function reducer(draft, action) {
switch (action.type) {
case "reset":
return initialState;
case "increment":
return void draft.count++;
case "decrement":
return void draft.count--;
}
}
function Counter() {
const [state, dispatch] = useImmerReducer(reducer, initialState);
return (
<>
Count: {state.count}
<button onClick={() => dispatch({ type: "reset" })}>Reset</button>
<button onClick={() => dispatch({ type: "increment" })}>+</button>
<button onClick={() => dispatch({ type: "decrement" })}>-</button>
</>
);
}
Top Related Projects
The official, opinionated, batteries-included toolset for efficient Redux development
🐻 Bear necessities for state management in React
Simple, scalable state management.
👻 Primitive and flexible state management for React
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual Copilot