0
# Actions
1
2
Comprehensive set of built-in actions for context assignment, event handling, actor communication, and lifecycle management. Actions are declarative and provide the building blocks for state machine behaviors.
3
4
## Capabilities
5
6
### Context Assignment
7
8
Updates the current context of the machine with new values, supporting both object assignment and functional updates.
9
10
```typescript { .api }
11
/**
12
* Updates the current context of the machine
13
* @param assignment - Object with new context values or function returning context update
14
* @returns ActionFunction that updates machine context
15
*/
16
function assign<TContext, TExpressionEvent extends EventObject>(
17
assignment:
18
| Assigner<TContext, TExpressionEvent>
19
| PartialAssigner<TContext, TExpressionEvent>
20
| PropertyAssigner<TContext, TExpressionEvent>
21
): ActionFunction<TContext, TExpressionEvent>;
22
23
type Assigner<TContext, TExpressionEvent extends EventObject> = (args: {
24
context: TContext;
25
event: TExpressionEvent;
26
spawn: Spawner;
27
}) => TContext;
28
29
type PartialAssigner<TContext, TExpressionEvent extends EventObject> = (args: {
30
context: TContext;
31
event: TExpressionEvent;
32
spawn: Spawner;
33
}) => Partial<TContext>;
34
35
type PropertyAssigner<TContext, TExpressionEvent extends EventObject> = {
36
[K in keyof TContext]?:
37
| TContext[K]
38
| ((args: { context: TContext; event: TExpressionEvent; spawn: Spawner }) => TContext[K]);
39
};
40
41
interface AssignAction extends ActionFunction<any, any> {
42
type: "xstate.assign";
43
}
44
```
45
46
**Usage Examples:**
47
48
```typescript
49
import { assign } from "xstate/actions";
50
51
// Object-based assignment
52
assign({ count: 42, name: "Updated" })
53
54
// Function-based assignment
55
assign(({ context, event }) => ({
56
count: context.count + 1,
57
lastEvent: event.type
58
}))
59
60
// Property-based assignment
61
assign({
62
count: ({ context }) => context.count + 1,
63
timestamp: () => Date.now()
64
})
65
66
// Partial assignment
67
assign(({ context, event }) => {
68
if (event.type === "INCREMENT") {
69
return { count: context.count + 1 };
70
}
71
return {};
72
})
73
```
74
75
### Event Handling
76
77
Actions for raising internal events and emitting external events.
78
79
```typescript { .api }
80
/**
81
* Raises an event to the internal event queue for immediate processing
82
* @param eventOrExpr - Event object or function returning event
83
* @param options - Optional configuration with id and delay
84
* @returns ActionFunction that raises internal events
85
*/
86
function raise<TEvent extends EventObject>(
87
eventOrExpr: TEvent | ((args: ActionArgs<any, any>) => TEvent),
88
options?: { id?: string; delay?: number }
89
): ActionFunction<any, any>;
90
91
/**
92
* Emits an event to external event handlers registered on the actor
93
* @param eventOrExpr - Event object or function returning event to emit
94
* @returns ActionFunction that emits events to external listeners
95
*/
96
function emit<TEvent extends EventObject>(
97
eventOrExpr: TEvent | ((args: ActionArgs<any, any>) => TEvent)
98
): ActionFunction<any, any>;
99
100
interface RaiseAction extends ActionFunction<any, any> {
101
type: "xstate.raise";
102
}
103
104
interface EmitAction extends ActionFunction<any, any> {
105
type: "xstate.emit";
106
}
107
```
108
109
**Usage Examples:**
110
111
```typescript
112
import { raise, emit } from "xstate/actions";
113
114
// Raise internal event
115
raise({ type: "INTERNAL_UPDATE", data: "processed" })
116
117
// Raise with delay
118
raise({ type: "TIMEOUT" }, { delay: 1000 })
119
120
// Emit external event
121
emit({ type: "STATUS_CHANGED", status: "ready" })
122
123
// Dynamic event creation
124
raise(({ context, event }) => ({
125
type: "COMPUTED",
126
result: context.value * 2
127
}))
128
```
129
130
### Actor Communication
131
132
Actions for sending events between actors and managing parent-child communication.
133
134
```typescript { .api }
135
/**
136
* Sends an event to a specific actor
137
* @param actor - Target actor reference, string ID, or function resolving to actor
138
* @param eventOrExpr - Event object or function returning event to send
139
* @param options - Optional configuration with id and delay
140
* @returns ActionFunction that sends events to other actors
141
*/
142
function sendTo<TTargetActor extends AnyActorRef, TEvent extends EventObject>(
143
actor: TTargetActor | string | ((args: ActionArgs<any, any>) => TTargetActor | string),
144
eventOrExpr: TEvent | ((args: ActionArgs<any, any>) => TEvent),
145
options?: { id?: string; delay?: number }
146
): ActionFunction<any, any>;
147
148
/**
149
* Sends an event to the parent machine
150
* @param eventOrExpr - Event object or function returning event to send
151
* @param options - Optional configuration with id and delay
152
* @returns ActionFunction for parent communication
153
*/
154
function sendParent<TEvent extends EventObject>(
155
eventOrExpr: TEvent | ((args: ActionArgs<any, any>) => TEvent),
156
options?: { id?: string; delay?: number }
157
): ActionFunction<any, any>;
158
159
/**
160
* Forwards the current event to a target actor
161
* @param actor - Target actor reference, string ID, or function resolving to actor
162
* @param options - Optional configuration with id and delay
163
* @returns ActionFunction that forwards events without modification
164
*/
165
function forwardTo<TTargetActor extends AnyActorRef>(
166
actor: TTargetActor | string | ((args: ActionArgs<any, any>) => TTargetActor | string),
167
options?: { id?: string; delay?: number }
168
): ActionFunction<any, any>;
169
170
interface SendToAction extends ActionFunction<any, any> {
171
type: "xstate.sendTo";
172
}
173
```
174
175
**Usage Examples:**
176
177
```typescript
178
import { sendTo, sendParent, forwardTo } from "xstate/actions";
179
180
// Send to specific actor
181
sendTo("childActor", { type: "UPDATE", data: "new value" })
182
183
// Send with delay
184
sendTo("childActor", { type: "DELAYED_UPDATE" }, { delay: 2000 })
185
186
// Send to parent
187
sendParent({ type: "CHILD_COMPLETED", result: "success" })
188
189
// Dynamic actor resolution
190
sendTo(
191
({ context }) => context.targetActor,
192
({ event }) => ({ type: "FORWARDED", original: event })
193
)
194
195
// Forward current event
196
forwardTo("childActor")
197
```
198
199
### Actor Lifecycle
200
201
Actions for spawning and stopping child actors.
202
203
```typescript { .api }
204
/**
205
* Spawns a child actor
206
* @param src - Actor logic or string reference to actor logic
207
* @param options - Optional configuration with id, systemId, input, and syncSnapshot
208
* @returns ActionFunction that creates and starts child actors
209
*/
210
function spawnChild<TActorLogic extends AnyActorLogic>(
211
src: TActorLogic | string,
212
options?: {
213
id?: string | ((args: ActionArgs<any, any>) => string);
214
systemId?: string;
215
input?: InputFrom<TActorLogic> | ((args: ActionArgs<any, any>) => InputFrom<TActorLogic>);
216
syncSnapshot?: boolean;
217
}
218
): ActionFunction<any, any>;
219
220
/**
221
* Stops a child actor
222
* @param actorRef - Actor reference, string ID, or function resolving to actor
223
* @returns ActionFunction that stops and removes child actors
224
*/
225
function stopChild(
226
actorRef: AnyActorRef | string | ((args: ActionArgs<any, any>) => AnyActorRef | string)
227
): ActionFunction<any, any>;
228
229
/**
230
* Deprecated alias for stopChild
231
* @deprecated Use stopChild instead
232
*/
233
function stop(
234
actorRef: AnyActorRef | string | ((args: ActionArgs<any, any>) => AnyActorRef | string)
235
): ActionFunction<any, any>;
236
237
interface SpawnAction extends ActionFunction<any, any> {
238
type: "xstate.spawnChild";
239
}
240
241
interface StopAction extends ActionFunction<any, any> {
242
type: "xstate.stopChild";
243
}
244
```
245
246
**Usage Examples:**
247
248
```typescript
249
import { spawnChild, stopChild } from "xstate/actions";
250
251
// Spawn child actor
252
spawnChild("childLogic", {
253
id: "child-1",
254
input: { initialValue: 100 }
255
})
256
257
// Dynamic spawning
258
spawnChild(
259
({ context }) => context.childLogic,
260
{
261
id: ({ event }) => `child-${event.childId}`,
262
input: ({ context, event }) => ({
263
parentContext: context,
264
eventData: event.data
265
})
266
}
267
)
268
269
// Stop child actor
270
stopChild("child-1")
271
272
// Dynamic stopping
273
stopChild(({ context }) => context.activeChild)
274
```
275
276
### Execution Control
277
278
Actions for controlling action execution flow and canceling delayed actions.
279
280
```typescript { .api }
281
/**
282
* Creates an action that executes dynamically queued actions
283
* @param collect - Function that receives enqueue function and action context
284
* @returns ActionFunction that allows conditional action execution
285
*/
286
function enqueueActions<TContext, TExpressionEvent extends EventObject>(
287
collect: (
288
enqueue: {
289
assign: typeof assign;
290
raise: typeof raise;
291
sendTo: typeof sendTo;
292
sendParent: typeof sendParent;
293
spawnChild: typeof spawnChild;
294
stopChild: typeof stopChild;
295
cancel: typeof cancel;
296
log: typeof log;
297
emit: typeof emit;
298
},
299
params: {
300
context: TContext;
301
event: TExpressionEvent;
302
check: (guard: any) => boolean;
303
self: AnyActorRef;
304
}
305
) => void
306
): ActionFunction<TContext, TExpressionEvent>;
307
308
/**
309
* Cancels a delayed sendTo action that is waiting to be executed
310
* @param sendId - ID of the sendTo action to cancel (string or function returning string)
311
* @returns ActionFunction that cancels scheduled delayed actions
312
*/
313
function cancel(
314
sendId: string | ((args: ActionArgs<any, any>) => string)
315
): ActionFunction<any, any>;
316
317
interface EnqueueActionsAction extends ActionFunction<any, any> {
318
type: "xstate.enqueueActions";
319
}
320
321
interface CancelAction extends ActionFunction<any, any> {
322
type: "xstate.cancel";
323
}
324
```
325
326
**Usage Examples:**
327
328
```typescript
329
import { enqueueActions, cancel } from "xstate/actions";
330
331
// Dynamic action execution
332
enqueueActions(({ enqueue, check, context, event }) => {
333
if (check("isValidUser")) {
334
enqueue.assign({ lastLogin: Date.now() });
335
enqueue.sendParent({ type: "USER_VALIDATED" });
336
} else {
337
enqueue.raise({ type: "VALIDATION_FAILED" });
338
}
339
340
if (context.shouldNotify) {
341
enqueue.emit({ type: "LOGIN_ATTEMPT", user: context.user });
342
}
343
})
344
345
// Cancel delayed action
346
cancel("delayed-notification")
347
348
// Dynamic cancellation
349
cancel(({ context }) => context.pendingActionId)
350
```
351
352
### Debugging
353
354
Actions for logging and debugging state machine execution.
355
356
```typescript { .api }
357
/**
358
* Logs values during action execution
359
* @param value - Value to log (string or function returning value)
360
* @param label - Optional label for the log entry
361
* @returns ActionFunction that logs to the actor's logger
362
*/
363
function log<TContext, TExpressionEvent extends EventObject>(
364
value?: string | ((args: ActionArgs<TContext, TExpressionEvent>) => any),
365
label?: string
366
): ActionFunction<TContext, TExpressionEvent>;
367
368
interface LogAction extends ActionFunction<any, any> {
369
type: "xstate.log";
370
}
371
```
372
373
**Usage Examples:**
374
375
```typescript
376
import { log } from "xstate/actions";
377
378
// Simple logging
379
log("State machine started")
380
381
// Dynamic logging
382
log(({ context, event }) => `User ${context.userId} performed ${event.type}`)
383
384
// Labeled logging
385
log(({ context }) => context.debugInfo, "Debug Info")
386
387
// Log context and event (default behavior)
388
log()
389
```
390
391
## Action Types and Utilities
392
393
```typescript { .api }
394
interface ActionArgs<TContext, TExpressionEvent extends EventObject> {
395
/** Current machine context */
396
context: TContext;
397
/** Current event being processed */
398
event: TExpressionEvent;
399
/** Reference to the current actor */
400
self: AnyActorRef;
401
/** Function for spawning child actors */
402
spawn: Spawner;
403
}
404
405
interface ActionFunction<TContext, TExpressionEvent extends EventObject> {
406
(args: ActionArgs<TContext, TExpressionEvent>): void;
407
/** Action type identifier */
408
type?: string;
409
/** Resolve dynamic parameters */
410
resolve?: (args: ActionArgs<TContext, TExpressionEvent>) => any;
411
}
412
413
type Actions<TContext, TEvent extends EventObject, TAction> =
414
| Action<TContext, TEvent, TAction>
415
| Action<TContext, TEvent, TAction>[];
416
417
type Action<TContext, TEvent extends EventObject, TAction> =
418
| TAction
419
| ActionFunction<TContext, TEvent>
420
| { type: string; [key: string]: any };
421
422
interface Spawner {
423
<TActorLogic extends AnyActorLogic>(
424
src: TActorLogic,
425
options?: {
426
id?: string;
427
input?: InputFrom<TActorLogic>;
428
syncSnapshot?: boolean;
429
}
430
): ActorRefFrom<TActorLogic>;
431
}
432
433
type UnknownAction = Action<any, any, any>;
434
```
435
436
## Best Practices
437
438
**Action Composition:**
439
```typescript
440
// Combine multiple actions
441
entry: [
442
assign({ status: "loading" }),
443
log("Starting data fetch"),
444
spawnChild("fetchLogic", { id: "fetcher" })
445
]
446
447
// Use enqueueActions for conditional logic
448
entry: enqueueActions(({ enqueue, check, context }) => {
449
enqueue.assign({ startTime: Date.now() });
450
451
if (check("shouldLog")) {
452
enqueue.log("Process started");
453
}
454
455
if (context.enableNotifications) {
456
enqueue.emit({ type: "PROCESS_STARTED" });
457
}
458
})
459
```
460
461
**Dynamic Parameters:**
462
```typescript
463
// Actions with dynamic parameters
464
sendTo(
465
({ context }) => context.targetActor,
466
({ event, context }) => ({
467
type: "PROCESS_DATA",
468
data: event.data,
469
timestamp: context.currentTime
470
}),
471
{ delay: ({ context }) => context.processingDelay }
472
)
473
```