Skip to content

Commit 420e5ff

Browse files
committed
Move TypeScript recipe patterns to TypeScript docs including crosslink
1 parent 339e8d3 commit 420e5ff

2 files changed

Lines changed: 64 additions & 64 deletions

File tree

website/docs/produce.mdx

Lines changed: 2 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@ title: Using produce
99
data-ea-type="image"
1010
className="horizontal bordered"
1111
></div>
12-
</center>
13-
<details>
12+
</center> <details>
1413
<summary className="egghead-summary">
1514
egghead.io lesson 3: Simplifying deep updates with _produce_
1615
</summary>
@@ -81,66 +80,7 @@ expect(nextState[0]).toBe(baseState[0])
8180
expect(nextState[1]).not.toBe(baseState[1])
8281
```
8382

84-
## TypeScript example
85-
86-
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.
87-
88-
```ts
89-
import {produce} from "immer"
90-
import type {Draft} from "immer"
91-
92-
type Recipe<S> = (draft: Draft<S>) => void | S
93-
94-
function updateState<S>(
95-
setState: (updater: (prev: S) => S) => void,
96-
recipe: Recipe<S>
97-
) {
98-
setState(prev => produce(prev, recipe))
99-
}
100-
```
101-
102-
## React example
103-
104-
A common usage in React is to wrap `setState` with `produce` so callers can provide a typed recipe.
105-
106-
```tsx
107-
import {produce} from "immer"
108-
import type {Draft} from "immer"
109-
import {useCallback, useState} from "react"
110-
111-
type State = {
112-
foo: string
113-
bar: string
114-
}
115-
116-
const initialState: State = {
117-
foo: "",
118-
bar: ""
119-
}
120-
121-
type Recipe = (draft: Draft<State>) => void | State
122-
123-
export function Example() {
124-
const [state, setState] = useState<State>(initialState)
125-
126-
const updateState = useCallback((recipe: Recipe) => {
127-
setState(prev => produce(prev, recipe))
128-
}, [])
129-
130-
return (
131-
<button
132-
onClick={() => {
133-
updateState(draft => {
134-
draft.foo = "Muffin Man" // this recipe is typed
135-
})
136-
}}
137-
type="button"
138-
>
139-
Hello {state.foo}
140-
</button>
141-
)
142-
}
143-
```
83+
For advanced TypeScript typing scenarios, see [Using TypeScript or Flow](./typescript.mdx).
14484

14585
### Terminology
14686

website/docs/typescript.mdx

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@ sidebar_label: TypeScript / Flow
1010
data-ea-type="image"
1111
className="horizontal bordered"
1212
></div>
13-
</center>
14-
<details>
13+
</center> <details>
1514
<summary className="egghead-summary">
1615
egghead.io lesson 12: Immer + TypeScript
1716
</summary>
@@ -64,6 +63,67 @@ This ensures that the only place you can modify your state is in your produce ca
6463
2. You can use the utility type `Immutable` to recursively make an entire type tree read-only, e.g.: `type ReadonlyState = Immutable<State>`.
6564
3. Immer won't automatically wrap all returned types in `Immutable` if the original type of the input state wasn't immutable. This is to make sure it doesn't break code bases that don't use immutable types.
6665

66+
## Typing reusable recipes
67+
68+
If you want to pass a recipe callback around, you can type it using `Draft` and allow either `void` or a replacement state as the return value.
69+
70+
```ts
71+
import {produce} from "immer"
72+
import type {Draft} from "immer"
73+
74+
type Recipe<S> = (draft: Draft<S>) => void | S
75+
76+
function updateState<S>(
77+
setState: (updater: (prev: S) => S) => void,
78+
recipe: Recipe<S>
79+
) {
80+
setState(prev => produce(prev, recipe))
81+
}
82+
```
83+
84+
### React example
85+
86+
A common usage in React is to wrap `setState` with `produce` so callers can provide a typed recipe.
87+
88+
```tsx
89+
import {produce} from "immer"
90+
import type {Draft} from "immer"
91+
import {useCallback, useState} from "react"
92+
93+
type State = {
94+
foo: string
95+
bar: string
96+
}
97+
98+
const initialState: State = {
99+
foo: "",
100+
bar: ""
101+
}
102+
103+
type Recipe = (draft: Draft<State>) => void | State
104+
105+
export function Example() {
106+
const [state, setState] = useState<State>(initialState)
107+
108+
const updateState = useCallback((recipe: Recipe) => {
109+
setState(prev => produce(prev, recipe))
110+
}, [])
111+
112+
return (
113+
<button
114+
onClick={() => {
115+
updateState(draft => {
116+
draft.foo = "Muffin Man" // this recipe is typed
117+
})
118+
}}
119+
type="button"
120+
>
121+
Hello {state.foo}
122+
</button>
123+
)
124+
}
125+
```
126+
67127
## Tips for curried producers
68128

69129
We try to inference as much as possible. So if a curried producer is created and directly passed to another function, we can infer the type from there. This works well with for example React:

0 commit comments

Comments
 (0)