React hooks that integrate immer for immutable state management with familiar mutable syntax
npx @tessl/cli install tessl/npm-use-immer@0.11.0Use Immer provides React hooks that integrate immer for immutable state management. It enables developers to write state updates using familiar mutable syntax while maintaining React's immutability requirements, making complex state transformations more readable and less error-prone.
npm install immer use-immerimport { useImmer, useImmerReducer } from "use-immer";CommonJS:
const { useImmer, useImmerReducer } = require("use-immer");Import individual types:
import { useImmer, useImmerReducer, type DraftFunction, type ImmerHook, type ImmerReducer } from "use-immer";Import external types used in API signatures:
import type { Draft } from "immer";
import type { Dispatch } from "react";import React from "react";
import { useImmer } from "use-immer";
function PersonEditor() {
const [person, updatePerson] = useImmer({
name: "Michel",
age: 33,
address: {
city: "Amsterdam",
country: "Netherlands"
}
});
function updateName(name: string) {
updatePerson(draft => {
draft.name = name;
});
}
function moveToCity(city: string) {
updatePerson(draft => {
draft.address.city = city;
});
}
return (
<div>
<h1>{person.name} ({person.age})</h1>
<p>Lives in {person.address.city}, {person.address.country}</p>
<input
value={person.name}
onChange={e => updateName(e.target.value)}
/>
</div>
);
}React hook similar to useState but allows mutating a draft state using immer producer functions.
/**
* React hook similar to useState but with immer integration for immutable updates
* @param initialValue - Initial state value or function that returns initial state
* @returns Tuple of [currentState, updaterFunction]
*/
function useImmer<S = any>(initialValue: S | (() => S)): ImmerHook<S>;
type ImmerHook<S> = [S, Updater<S>];
type Updater<S> = (arg: S | DraftFunction<S>) => void;
type DraftFunction<S> = (draft: Draft<S>) => void;The useImmer hook accepts either a direct value or a producer function that receives a mutable draft:
Usage Examples:
import { useImmer } from "use-immer";
// Direct value updates (like useState)
const [count, setCount] = useImmer(0);
setCount(count + 1);
// Draft function updates (using immer)
const [user, updateUser] = useImmer({ name: "Alice", posts: [] });
updateUser(draft => {
draft.posts.push({ title: "New Post", content: "..." });
});
// Lazy initial state
const [expensiveState, updateExpensiveState] = useImmer(() => computeExpensiveInitialState());Immer-powered reducer hook based on React's useReducer, allowing reducers to mutate draft state.
/**
* Immer-powered reducer hook that allows reducers to mutate draft state
* @param reducer - Reducer function that operates on draft state
* @param initialState - Initial state value
* @returns Tuple of [state, dispatch]
*/
function useImmerReducer<S, A>(
reducer: ImmerReducer<S, A>,
initialState: S,
initializer?: undefined
): [S, Dispatch<A>];
/**
* Immer-powered reducer hook with initializer function
* @param reducer - Reducer function that operates on draft state
* @param initializerArg - Argument passed to initializer function
* @param initializer - Function to compute initial state
* @returns Tuple of [state, dispatch]
*/
function useImmerReducer<S, A, I>(
reducer: ImmerReducer<S, A>,
initializerArg: I,
initializer: (arg: I) => S
): [S, Dispatch<A>];
/**
* Immer-powered reducer hook with typed initializer argument
* @param reducer - Reducer function that operates on draft state
* @param initializerArg - Typed argument passed to initializer function
* @param initializer - Function to compute initial state from typed argument
* @returns Tuple of [state, dispatch]
*/
function useImmerReducer<S, A, I>(
reducer: ImmerReducer<S, A>,
initializerArg: S & I,
initializer: (arg: S & I) => S
): [S, Dispatch<A>];
type ImmerReducer<S, A> = (
draftState: Draft<S>,
action: A
) => void | (S extends undefined ? typeof nothing : S);Usage Examples:
import { useImmerReducer } from "use-immer";
// Define reducer that mutates draft state
function counterReducer(draft, action) {
switch (action.type) {
case "increment":
draft.count++;
break;
case "decrement":
draft.count--;
break;
case "reset":
return { count: 0 }; // Can still return new state
}
}
function Counter() {
const [state, dispatch] = useImmerReducer(counterReducer, { count: 0 });
return (
<div>
Count: {state.count}
<button onClick={() => dispatch({ type: "increment" })}>+</button>
<button onClick={() => dispatch({ type: "decrement" })}>-</button>
<button onClick={() => dispatch({ type: "reset" })}>Reset</button>
</div>
);
}
// Complex state example
interface TodoState {
todos: Array<{ id: number; text: string; completed: boolean }>;
filter: "all" | "active" | "completed";
}
function todoReducer(draft: TodoState, action: any) {
switch (action.type) {
case "add_todo":
draft.todos.push({
id: Date.now(),
text: action.text,
completed: false
});
break;
case "toggle_todo":
const todo = draft.todos.find(t => t.id === action.id);
if (todo) {
todo.completed = !todo.completed;
}
break;
case "set_filter":
draft.filter = action.filter;
break;
}
}/**
* Function type for immer draft manipulation
*/
type DraftFunction<S> = (draft: Draft<S>) => void;
/**
* Updater function that accepts either a value or a draft function
* When passed a function, it's treated as a draft function and passed to immer's produce
* When passed a value, it replaces the state directly (like useState)
*/
type Updater<S> = (arg: S | DraftFunction<S>) => void;
/**
* Return type for useImmer hook - tuple of state and updater
*/
type ImmerHook<S> = [S, Updater<S>];
/**
* Reducer function type that operates on immer draft state
*/
type ImmerReducer<S, A> = (
draftState: Draft<S>,
action: A
) => void | (S extends undefined ? typeof nothing : S);
/**
* @deprecated Use ImmerReducer instead since there is already a Reducer type in @types/react
*/
type Reducer<S = any, A = any> = ImmerReducer<S, A>;These types are imported from external dependencies and used in the API signatures:
/**
* From immer: Mutable proxy type for immutable state updates
*/
type Draft<T> = T; // Immer's Draft type - provides mutable interface to immutable data
/**
* From immer: Sentinel value for indicating deletion in draft operations
*/
const nothing: unique symbol;
/**
* From react: Dispatch function type for useReducer hook
*/
type Dispatch<A> = (value: A) => void;This package requires the following peer dependencies:
>=8.0.0 - Core immer functionality for immutable updates^16.8.0 || ^17.0.1 || ^18.0.0 || ^19.0.0 - React hooks supportThe following immer types and functions are re-exported and used internally:
Draft<T> - Immer draft type for mutable state proxiesnothing - Immer sentinel value for indicating deletionfreeze - Immer function for creating immutable objectsproduce - Immer function for creating new state from mutationsThe hooks follow React's error handling patterns: