0
# Context Utilities
1
2
The context utilities provide a way to share XState actors across React component trees using React Context. This is ideal for global state management and avoiding prop drilling.
3
4
## createActorContext
5
6
Creates a React context with provider and context-bound hooks for sharing an XState actor across components.
7
8
```typescript { .api }
9
function createActorContext<TLogic extends AnyActorLogic>(
10
actorLogic: TLogic,
11
actorOptions?: ActorOptions<TLogic>
12
): {
13
useSelector: <T>(
14
selector: (snapshot: SnapshotFrom<TLogic>) => T,
15
compare?: (a: T, b: T) => boolean
16
) => T;
17
useActorRef: () => Actor<TLogic>;
18
Provider: React.ComponentType<{
19
children: React.ReactNode;
20
options?: ActorOptions<TLogic>;
21
logic?: TLogic;
22
}>;
23
};
24
```
25
26
### Parameters
27
28
- `actorLogic`: The XState actor logic to be shared
29
- `actorOptions`: Default actor configuration options
30
31
### Returns
32
33
An object containing:
34
- `useSelector`: Context-bound selector hook
35
- `useActorRef`: Context-bound actor reference hook
36
- `Provider`: React component to provide the actor context
37
38
### Usage Example
39
40
```typescript
41
import { createActorContext } from "@xstate/react";
42
import { createMachine, assign } from "xstate";
43
44
// Define your machine
45
const authMachine = createMachine({
46
id: "auth",
47
initial: "loggedOut",
48
context: {
49
user: null,
50
token: null
51
},
52
states: {
53
loggedOut: {
54
on: {
55
LOGIN: {
56
target: "loggedIn",
57
actions: assign({
58
user: ({ event }) => event.user,
59
token: ({ event }) => event.token
60
})
61
}
62
}
63
},
64
loggedIn: {
65
on: {
66
LOGOUT: {
67
target: "loggedOut",
68
actions: assign({
69
user: null,
70
token: null
71
})
72
}
73
}
74
}
75
}
76
});
77
78
// Create the context
79
const AuthContext = createActorContext(authMachine);
80
81
// App component with provider
82
function App() {
83
return (
84
<AuthContext.Provider>
85
<Navigation />
86
<MainContent />
87
</AuthContext.Provider>
88
);
89
}
90
91
// Components that use the context
92
function Navigation() {
93
const user = AuthContext.useSelector((state) => state.context.user);
94
const isLoggedIn = AuthContext.useSelector((state) => state.matches("loggedIn"));
95
const authActor = AuthContext.useActorRef();
96
97
if (!isLoggedIn) {
98
return (
99
<nav>
100
<button onClick={() => authActor.send({
101
type: "LOGIN",
102
user: { name: "John" },
103
token: "abc123"
104
})}>
105
Login
106
</button>
107
</nav>
108
);
109
}
110
111
return (
112
<nav>
113
<span>Welcome, {user.name}!</span>
114
<button onClick={() => authActor.send({ type: "LOGOUT" })}>
115
Logout
116
</button>
117
</nav>
118
);
119
}
120
121
function MainContent() {
122
const isLoggedIn = AuthContext.useSelector((state) => state.matches("loggedIn"));
123
124
return (
125
<main>
126
{isLoggedIn ? <Dashboard /> : <LoginPrompt />}
127
</main>
128
);
129
}
130
```
131
132
## Provider Component
133
134
The Provider component created by `createActorContext` accepts the following props:
135
136
```typescript { .api }
137
interface ProviderProps<TLogic extends AnyActorLogic> {
138
children: React.ReactNode;
139
options?: ActorOptions<TLogic>;
140
logic?: TLogic;
141
/** @deprecated Use `logic` instead. */
142
machine?: never;
143
}
144
```
145
146
### Props
147
148
- `children`: React children to receive the context
149
- `options`: Optional actor configuration options (overrides defaults)
150
- `logic`: Optional different actor logic (overrides default)
151
152
### Usage with Custom Options
153
154
```typescript
155
function App() {
156
return (
157
<AuthContext.Provider
158
options={{
159
input: { apiUrl: "https://api.example.com" }
160
}}
161
>
162
<AppContent />
163
</AuthContext.Provider>
164
);
165
}
166
```
167
168
### Usage with Different Logic
169
170
```typescript
171
const devAuthMachine = createMachine({
172
// Development version with mock data
173
});
174
175
function App() {
176
const isDevelopment = process.env.NODE_ENV === "development";
177
178
return (
179
<AuthContext.Provider
180
logic={isDevelopment ? devAuthMachine : authMachine}
181
>
182
<AppContent />
183
</AuthContext.Provider>
184
);
185
}
186
```
187
188
## Context-bound Hooks
189
190
### useSelector
191
192
The context-bound `useSelector` hook works identically to the standalone version but automatically uses the actor from context.
193
194
```typescript { .api }
195
const contextUseSelector: <T>(
196
selector: (snapshot: SnapshotFrom<TLogic>) => T,
197
compare?: (a: T, b: T) => boolean
198
) => T;
199
```
200
201
### useActorRef
202
203
The context-bound `useActorRef` hook returns the actor reference from context.
204
205
```typescript { .api }
206
const contextUseActorRef: () => Actor<TLogic>;
207
```
208
209
## Error Handling
210
211
The context hooks will throw an error if used outside of their corresponding Provider:
212
213
```typescript
214
function ComponentOutsideProvider() {
215
// This will throw an error
216
const user = AuthContext.useSelector((state) => state.context.user);
217
// Error: You used a hook from "ActorProvider" but it's not inside a <ActorProvider> component.
218
}
219
```
220
221
## Multiple Contexts
222
223
You can create and use multiple actor contexts in the same application:
224
225
```typescript
226
const AuthContext = createActorContext(authMachine);
227
const ThemeContext = createActorContext(themeMachine);
228
const CartContext = createActorContext(cartMachine);
229
230
function App() {
231
return (
232
<AuthContext.Provider>
233
<ThemeContext.Provider>
234
<CartContext.Provider>
235
<AppContent />
236
</CartContext.Provider>
237
</ThemeContext.Provider>
238
</AuthContext.Provider>
239
);
240
}
241
242
function AppContent() {
243
const user = AuthContext.useSelector((state) => state.context.user);
244
const theme = ThemeContext.useSelector((state) => state.context.theme);
245
const cartItems = CartContext.useSelector((state) => state.context.items);
246
247
// Use all three contexts...
248
}
249
```
250
251
## Common Patterns
252
253
### Combining with Custom Hooks
254
255
```typescript
256
// Custom hook for auth operations
257
function useAuth() {
258
const user = AuthContext.useSelector((state) => state.context.user);
259
const isLoggedIn = AuthContext.useSelector((state) => state.matches("loggedIn"));
260
const authActor = AuthContext.useActorRef();
261
262
const login = useCallback((credentials) => {
263
authActor.send({ type: "LOGIN", ...credentials });
264
}, [authActor]);
265
266
const logout = useCallback(() => {
267
authActor.send({ type: "LOGOUT" });
268
}, [authActor]);
269
270
return { user, isLoggedIn, login, logout };
271
}
272
```
273
274
### Performance Optimization
275
276
```typescript
277
// Memoize selectors for complex computations
278
const memoizedSelector = useMemo(
279
() => (state) => expensiveComputation(state.context.data),
280
[]
281
);
282
283
const result = AuthContext.useSelector(memoizedSelector);
284
```
285
286
### Conditional Context Usage
287
288
```typescript
289
function FeatureComponent({ useGlobalState }: { useGlobalState: boolean }) {
290
if (useGlobalState) {
291
const data = GlobalContext.useSelector((state) => state.context.data);
292
return <div>{data}</div>;
293
}
294
295
// Use local state instead
296
const [localData] = useState("local");
297
return <div>{localData}</div>;
298
}
299
```