From 339e8d3f5c4a37a595c41f6fc2e49ce933d67523 Mon Sep 17 00:00:00 2001 From: Dean Kerr Date: Mon, 11 May 2026 15:56:59 +0100 Subject: [PATCH] add typed custom produce function --- website/docs/produce.mdx | 61 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/website/docs/produce.mdx b/website/docs/produce.mdx index b1747689..ec55f22b 100644 --- a/website/docs/produce.mdx +++ b/website/docs/produce.mdx @@ -81,6 +81,67 @@ expect(nextState[0]).toBe(baseState[0]) expect(nextState[1]).not.toBe(baseState[1]) ``` +## TypeScript example + +If you want to pass a typed recipe callback around, you can type it using `Draft` and allow either `void` or a replacement state as the return value. + +```ts +import {produce} from "immer" +import type {Draft} from "immer" + +type Recipe = (draft: Draft) => void | S + +function updateState( + setState: (updater: (prev: S) => S) => void, + recipe: Recipe +) { + setState(prev => produce(prev, recipe)) +} +``` + +## React example + +A common usage in React is to wrap `setState` with `produce` so callers can provide a typed recipe. + +```tsx +import {produce} from "immer" +import type {Draft} from "immer" +import {useCallback, useState} from "react" + +type State = { + foo: string + bar: string +} + +const initialState: State = { + foo: "", + bar: "" +} + +type Recipe = (draft: Draft) => void | State + +export function Example() { + const [state, setState] = useState(initialState) + + const updateState = useCallback((recipe: Recipe) => { + setState(prev => produce(prev, recipe)) + }, []) + + return ( + + ) +} +``` + ### Terminology - `(base)state`, the immutable state passed to `produce`