You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: src/content/reference/react/useActionState.md
+46-45Lines changed: 46 additions & 45 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -4,7 +4,7 @@ title: useActionState
4
4
5
5
<Intro>
6
6
7
-
`useActionState` is a React Hook that updates state with side effects using [Actions](/reference/react/useTransition#functions-called-in-starttransition-are-called-actions).
7
+
`useActionState` is a React Hook that lets you update state with side effects using [Actions](/reference/react/useTransition#functions-called-in-starttransition-are-called-actions).
@@ -51,17 +51,17 @@ function MyCart({initialState}) {
51
51
52
52
1. The current state. During the first render, it will match the `initialState` you passed. After `dispatchAction` is invoked, it will match the value returned by the `reducerAction`.
53
53
2. A `dispatchAction` function that you call inside [Actions](/reference/react/useTransition#functions-called-in-starttransition-are-called-actions).
54
-
3. The `isPending` flag that tells you if any dispatched Actions for this hook are pending.
54
+
3. The `isPending` flag that tells you if any dispatched Actions for this Hook are pending.
55
55
56
56
#### Caveats {/*caveats*/}
57
57
58
-
* `useActionState` is a Hook, so it must be called **at the top level of your component** or your own Hooks. You can't call it inside loops or conditions. If you need that, extract a new component and move the state into it.
58
+
* `useActionState` is a Hook, so you can only call it **at the top level of your component** or your own Hooks. You can't call it inside loops or conditions. If you need that, extract a new component and move the state into it.
59
59
* React queues and executes multiple calls to `dispatchAction` sequentially, allowing each `reducerAction` to use the result of the previous Action.
60
60
* The `dispatchAction` function has a stable identity, so you will often see it omitted from Effect dependencies, but including it will not cause the Effect to fire. If the linter lets you omit a dependency without errors, it is safe to do. [Learn more about removing Effect dependencies.](/learn/removing-effect-dependencies#move-dynamic-objects-and-functions-inside-your-effect)
61
61
* When using the `permalink` option, ensure the same form component is rendered on the destination page (including the same `reducerAction` and `permalink`) so React knows how to pass the state through. Once the page becomes interactive, this parameter has no effect.
62
62
* When using Server Functions, `initialState` needs to be [serializable](/reference/rsc/use-server#serializable-parameters-and-return-values) (values like plain objects, arrays, strings, and numbers).
63
63
* If `dispatchAction` throws an error, React cancels all queued actions and shows the nearest [Error Boundary](/reference/react/Component#catching-rendering-errors-with-an-error-boundary).
64
-
* If there are multiple ongoing Actions, React currently batches them together. This is a limitation that may be removed in a future release.
64
+
* If there are multiple ongoing Actions, React batches them together. This is a limitation that may be removed in a future release.
65
65
66
66
<Note>
67
67
@@ -86,7 +86,7 @@ async function reducerAction(previousState, actionPayload) {
86
86
}
87
87
```
88
88
89
-
Each time you call `dispatchAction`, React calls the `reducerAction` with the `actionPayload`. The reducer will perform side effects such as posting data, and return the new state. If `dispatchAction` is called multiple times, React queues and executes them in order so the result of the previous call is available for current call.
89
+
Each time you call `dispatchAction`, React calls the `reducerAction` with the `actionPayload`. The reducer will perform side effects such as posting data, and return the new state. If `dispatchAction` is called multiple times, React queues and executes them in order so the result of the previous call is available for the current call.
90
90
91
91
#### Parameters {/*reduceraction-parameters*/}
92
92
@@ -101,7 +101,7 @@ Each time you call `dispatchAction`, React calls the `reducerAction` with the `a
101
101
#### Caveats {/*reduceraction-caveats*/}
102
102
103
103
* `reducerAction` can be sync or async. It can perform sync actions like showing a notification, or async actions like posting updates to a server.
104
-
* `reducerAction` is not invoked twice in StrictMode since `reducerAction` is designed to allow side effects.
104
+
* `reducerAction` is not invoked twice in `<StrictMode>` since `reducerAction` is designed to allow side effects.
105
105
* The return type of `reducerAction` must match the type of `initialState`. If TypeScript infers a mismatch, you may need to explicitly annotate your state type.
106
106
* If you set state after `await` in the `reducerAction` you currently need to wrap the state update in an additional `startTransition`. See the [startTransition](/reference/react/useTransition#react-doesnt-treat-my-state-update-after-await-as-a-transition) docs for more info.
107
107
@@ -112,7 +112,7 @@ Each time you call `dispatchAction`, React calls the `reducerAction` with the `a
112
112
The function passed to `useActionState` is called a *reducer action* because:
113
113
114
114
- It *reduces* the previous state into a new state, like `useReducer`.
115
-
- It an *Action* because it's called inside a Transition and can perform side effects.
115
+
- It's an *Action* because it's called inside a Transition and can perform side effects.
116
116
117
117
Conceptually, `useActionState` is like `useReducer`, but you can do side effects in the reducer.
118
118
@@ -145,13 +145,13 @@ function Counter() {
145
145
2. The <CodeStep step={2}>action dispatcher</CodeStep> that lets you trigger `reducerAction`.
146
146
3. A <CodeStep step={3}>pending state</CodeStep> that tells you whether the Action is in progress.
147
147
148
-
To call `addToCartAction`, call the <CodeStep step={2}>action dispatcher</CodeStep>. React will call queue calls to `addToCartAction` with the previous count.
148
+
To call `addToCartAction`, call the <CodeStep step={2}>action dispatcher</CodeStep>. React will queue calls to `addToCartAction` with the previous count.
@@ -257,16 +257,18 @@ Every time you click "Add Ticket," React queues a call to `addToCartAction`. Rea
257
257
258
258
#### How `useActionState` queuing works {/*how-useactionstate-queuing-works*/}
259
259
260
-
Try clicking "Add Ticket" multiple times. Every time you click, a new `addToCart` is queued. Since there's an artificial 1 second delay, that means 4 clicks will take ~4 seconds to complete.
260
+
Try clicking "Add Ticket" multiple times. Every time you click, a new `addToCartAction` is queued. Since there's an artificial 1 second delay, that means 4 clicks will take ~4 seconds to complete.
261
261
262
262
**This is intentional in the design of `useActionState`.**
263
263
264
-
We have to wait for the previous result of `addToCart` in order to pass the `prevCount` to the next call to `addToCart`. That means React has to wait for the previous Action to finish before calling the next Action.
264
+
We have to wait for the previous result of `addToCartAction` in order to pass the `prevCount` to the next call to `addToCartAction`. That means React has to wait for the previous Action to finish before calling the next Action.
265
265
266
266
You can typically solve this by [using with useOptimistic](/reference/react/useActionState#using-with-useoptimistic) but for more complex cases you may want to consider [cancelling queued actions](#cancelling-queued-actions) or not using `useActionState`.
267
267
268
268
</DeepDive>
269
269
270
+
---
271
+
270
272
### Using multiple Action types {/*using-multiple-action-types*/}
271
273
272
274
To handle multiple types, you can pass an argument to `dispatchAction`.
@@ -285,13 +287,13 @@ export default function Checkout() {
285
287
286
288
functionhandleAdd() {
287
289
startTransition(() => {
288
-
dispatchAction({type:'ADD'});
290
+
dispatchAction({type:'ADD'});
289
291
});
290
292
}
291
293
292
294
functionhandleRemove() {
293
295
startTransition(() => {
294
-
dispatchAction({type:'REMOVE'});
296
+
dispatchAction({type:'REMOVE'});
295
297
});
296
298
}
297
299
@@ -431,7 +433,7 @@ You might notice this example looks a lot like `useReducer`, but they serve diff
431
433
432
434
- **Use `useActionState`** to manage state of your Actions. The reducer can perform side effects.
433
435
434
-
You can think of `useActionState` as `useReducer` for side effects from user Actions. Since it computes the next Action to take based on the previous Action, it has to [order the calls sequentially](/reference/react/useActionState#how-useactionstate-queuing-works). If you want to perform Action in parallel, use `useState` and `useTransition` directly.
436
+
You can think of `useActionState` as `useReducer` for side effects from user Actions. Since it computes the next Action to take based on the previous Action, it has to [order the calls sequentially](/reference/react/useActionState#how-useactionstate-queuing-works). If you want to perform Actions in parallel, use `useState` and `useTransition` directly.
435
437
436
438
</DeepDive>
437
439
@@ -802,15 +804,15 @@ hr {
802
804
</Sandpack>
803
805
804
806
805
-
When the stepper arrow is clicked, `setOptimisticCount` immediately updates the quantity, and `dispatchAction()` queues the `updateCartAction`. We show a pending indicator on both the quantity and total to give the user feedback that their update is still being applied.
807
+
When the stepper arrow is clicked, `setOptimisticCount` immediately updates the quantity, and `dispatchAction()` queues the `updateCartAction`. A pending indicator appears on both the quantity and total to give the user feedback that their update is still being applied.
806
808
807
809
---
808
810
809
-
### Using with `<form>`action props {/*use-with-a-form*/}
811
+
### Using with `<form>`Action props {/*use-with-a-form*/}
810
812
811
-
The `dispatchAction` function can be passed as the `action` prop to a `<form>`.
813
+
You can pass the `dispatchAction` function as the `action` prop to a `<form>`.
812
814
813
-
When used this way, React automatically wraps the submission in a transition, so you don't need to call `startTransition` yourself. The `reducerAction` receives the previous state and the submitted `FormData`:
815
+
When used this way, React automatically wraps the submission in a Transition, so you don't need to call `startTransition` yourself. The `reducerAction` receives the previous state and the submitted `FormData`:
814
816
815
817
<Sandpack>
816
818
@@ -974,7 +976,7 @@ See the [`<form>`](/reference/react-dom/components/form#handle-form-submission-w
Try clicking increase or decrease multiple times, and notice that the total updates within 1 second no matter how many times you click. This works because we're using an AbortController to "complete" the previous Action so the next Action can proceed.
1200
+
Try clicking increase or decrease multiple times, and notice that the total updates within 1 second no matter how many times you click. This works because it uses an `AbortController` to "complete" the previous Action so the next Action can proceed.
1200
1201
1201
1202
<Pitfall>
1202
1203
@@ -1210,7 +1211,7 @@ For example, if the Action performs a mutation (like writing to a database), abo
1210
1211
1211
1212
## Troubleshooting {/*troubleshooting*/}
1212
1213
1213
-
### My action can no longer read the submitted form data {/*action-cant-read-form-data*/}
1214
+
### My Action can no longer read the submitted form data {/*action-cant-read-form-data*/}
1214
1215
1215
1216
When you use `useActionState`, the `reducerAction` receives an extra argument as its first argument: the previous or initial state. The submitted form data is therefore its second argument instead of its first.
1216
1217
@@ -1294,7 +1295,7 @@ async function myReducerAction(prevState, data) {
1294
1295
1295
1296
---
1296
1297
1297
-
### I want to reset the state {/*reset-state*/}
1298
+
### My state doesn't reset {/*reset-state*/}
1298
1299
1299
1300
`useActionState` doesn't provide a built-in reset function. To reset the state, you can design your `reducerAction` to handle a reset signal:
0 commit comments