or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.md
tile.json

tessl/npm-use-immer

React hooks that integrate immer for immutable state management with familiar mutable syntax

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/use-immer@0.11.x

To install, run

npx @tessl/cli install tessl/npm-use-immer@0.11.0

index.mddocs/

Use Immer

Use 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.

Package Information

  • Package Name: use-immer
  • Package Type: npm
  • Language: TypeScript
  • Installation: npm install immer use-immer

Core Imports

import { 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";

Basic Usage

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>
  );
}

Capabilities

State Hook with Immer Integration

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());

Reducer Hook with Immer Integration

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;
  }
}

Types

/**
 * 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>;

External Types Referenced

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;

Dependencies

This package requires the following peer dependencies:

  • immer: >=8.0.0 - Core immer functionality for immutable updates
  • react: ^16.8.0 || ^17.0.1 || ^18.0.0 || ^19.0.0 - React hooks support

The following immer types and functions are re-exported and used internally:

  • Draft<T> - Immer draft type for mutable state proxies
  • nothing - Immer sentinel value for indicating deletion
  • freeze - Immer function for creating immutable objects
  • produce - Immer function for creating new state from mutations

Error Handling

The hooks follow React's error handling patterns:

  • Invalid initial state will throw during component initialization
  • Errors in draft functions are propagated as React errors
  • Type errors occur at compile time when using TypeScript with proper typings