@xstate/react 1.6.1 → 1.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +39 -0
- package/README.md +5 -512
- package/dist/xstate-react.umd.min.js +15 -2
- package/es/useInterpret.js +1 -1
- package/es/useSelector.js +46 -33
- package/lib/useInterpret.js +1 -1
- package/lib/useSelector.js +45 -32
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,44 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.6.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#2736](https://github.com/statelyai/xstate/pull/2736) [`2246ae051`](https://github.com/statelyai/xstate/commit/2246ae051663f261b4750d7adba57f008ec28f1d) Thanks [@Andarist](https://github.com/Andarist), [@davidkpiano](https://github.com/davidkpiano), [@VanTanev](https://github.com/VanTanev)! - The `useSelector(...)` hook now works as expected when the `actor` passed in changes. The hook will properly subscribe to the new `actor` and select the desired value. See [#2702](https://github.com/statelyai/xstate/issues/2702)
|
|
8
|
+
|
|
9
|
+
* [#2685](https://github.com/statelyai/xstate/pull/2685) [`469268d39`](https://github.com/statelyai/xstate/commit/469268d39fbc23996599773adfc4ca824b48585f) Thanks [@farskid](https://github.com/farskid), [@Andarist](https://github.com/Andarist)! - Fixed a regression with a development-only warning not being shown when a machine reference is updated during the hook lifecycle. This usually happens when machine options are dependent on external values and they're passed via `withConfig`.
|
|
10
|
+
|
|
11
|
+
```js
|
|
12
|
+
const machine = createMachine({
|
|
13
|
+
initial: 'foo',
|
|
14
|
+
context: { id: 1 },
|
|
15
|
+
states: {
|
|
16
|
+
foo: {
|
|
17
|
+
on: {
|
|
18
|
+
CHECK: {
|
|
19
|
+
target: 'bar',
|
|
20
|
+
cond: 'hasOverflown'
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
bar: {}
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const [id, setId] = useState(1);
|
|
29
|
+
const [current, send] = useMachine(
|
|
30
|
+
machine.withConfig({
|
|
31
|
+
guards: {
|
|
32
|
+
hasOverflown: () => id > 1 // id is a reference to an outside value
|
|
33
|
+
}
|
|
34
|
+
})
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
// later when id updates
|
|
38
|
+
setId(2);
|
|
39
|
+
// Now the reference passed to `useMachine` (the result of `machine.withConfig`) is updated but the interpreted machine stays the same. So the guard is still the previous one that got passed to the `useMachine` initially, and it closes over the stale `id`.
|
|
40
|
+
```
|
|
41
|
+
|
|
3
42
|
## 1.6.1
|
|
4
43
|
|
|
5
44
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
# @xstate/react
|
|
2
2
|
|
|
3
|
-
[[
|
|
3
|
+
This package contains utilities for using [XState](https://github.com/statelyai/xstate) with [React](https://github.com/facebook/react/).
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
- [Read the full documentation in the XState docs](https://xstate.js.org/docs/packages/xstate-react/).
|
|
6
|
+
- [Read our contribution guidelines](https://github.com/statelyai/xstate/blob/main/CONTRIBUTING.md).
|
|
7
|
+
|
|
8
|
+
## Quick start
|
|
6
9
|
|
|
7
10
|
1. Install `xstate` and `@xstate/react`:
|
|
8
11
|
|
|
@@ -57,513 +60,3 @@ export const Toggler = () => {
|
|
|
57
60
|
);
|
|
58
61
|
};
|
|
59
62
|
```
|
|
60
|
-
|
|
61
|
-
## Examples
|
|
62
|
-
|
|
63
|
-
- [XState + React TodoMVC (CodeSandbox)](https://codesandbox.io/s/xstate-todomvc-33wr94qv1)
|
|
64
|
-
|
|
65
|
-
## API
|
|
66
|
-
|
|
67
|
-
### `useMachine(machine, options?)`
|
|
68
|
-
|
|
69
|
-
A [React hook](https://reactjs.org/hooks) that interprets the given `machine` and starts a service that runs for the lifetime of the component.
|
|
70
|
-
|
|
71
|
-
**Arguments**
|
|
72
|
-
|
|
73
|
-
- `machine` - An [XState machine](https://xstate.js.org/docs/guides/machines.html) or a function that lazily returns a machine:
|
|
74
|
-
|
|
75
|
-
```js
|
|
76
|
-
// existing machine
|
|
77
|
-
const [state, send] = useMachine(machine);
|
|
78
|
-
|
|
79
|
-
// lazily-created machine
|
|
80
|
-
const [state, send] = useMachine(() =>
|
|
81
|
-
createMachine({
|
|
82
|
-
/* ... */
|
|
83
|
-
})
|
|
84
|
-
);
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
- `options` (optional) - [Interpreter options](https://xstate.js.org/docs/guides/interpretation.html#options) and/or any of the following machine config options: `guards`, `actions`, `services`, `delays`, `immediate`, `context`, `state`.
|
|
88
|
-
|
|
89
|
-
**Returns** a tuple of `[state, send, service]`:
|
|
90
|
-
|
|
91
|
-
- `state` - Represents the current state of the machine as an XState `State` object.
|
|
92
|
-
- `send` - A function that sends events to the running service.
|
|
93
|
-
- `service` - The created service.
|
|
94
|
-
|
|
95
|
-
### `useService(service)`
|
|
96
|
-
|
|
97
|
-
::: warning Deprecated
|
|
98
|
-
|
|
99
|
-
In the next major version, `useService(service)` will be replaced with `useActor(service)`. Prefer using the `useActor(service)` hook for services instead, since services are also actors.
|
|
100
|
-
|
|
101
|
-
Also, keep in mind that only a single argument (the event object) can be sent to `send(eventObject)` from `useActor(...)`. When migrating to `useActor(...)`, refactor `send(...)` calls to use only a single event object:
|
|
102
|
-
|
|
103
|
-
```diff
|
|
104
|
-
const [state, send] = useActor(service);
|
|
105
|
-
-send('CLICK', { x: 0, y: 3 });
|
|
106
|
-
+send({ type: 'CLICK', x: 0, y: 3 });
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
:::
|
|
110
|
-
|
|
111
|
-
A [React hook](https://reactjs.org/hooks) that subscribes to state changes from an existing [service](https://xstate.js.org/docs/guides/interpretation.html).
|
|
112
|
-
|
|
113
|
-
**Arguments**
|
|
114
|
-
|
|
115
|
-
- `service` - An [XState service](https://xstate.js.org/docs/guides/interpretation.html).
|
|
116
|
-
|
|
117
|
-
**Returns** a tuple of `[state, send]`:
|
|
118
|
-
|
|
119
|
-
- `state` - Represents the current state of the service as an XState `State` object.
|
|
120
|
-
- `send` - A function that sends events to the running service.
|
|
121
|
-
|
|
122
|
-
### `useActor(actor, getSnapshot?)`
|
|
123
|
-
|
|
124
|
-
A [React hook](https://reactjs.org/hooks) that subscribes to emitted changes from an existing [actor](https://xstate.js.org/docs/guides/actors.html).
|
|
125
|
-
|
|
126
|
-
**Arguments**
|
|
127
|
-
|
|
128
|
-
- `actor` - an actor-like object that contains `.send(...)` and `.subscribe(...)` methods.
|
|
129
|
-
- `getSnapshot` - a function that should return the latest emitted value from the `actor`.
|
|
130
|
-
- Defaults to attempting to get the `actor.state`, or returning `undefined` if that does not exist.
|
|
131
|
-
|
|
132
|
-
```js
|
|
133
|
-
const [state, send] = useActor(someSpawnedActor);
|
|
134
|
-
|
|
135
|
-
// with custom actors
|
|
136
|
-
const [state, send] = useActor(customActor, (actor) => {
|
|
137
|
-
// implementation-specific pseudocode example:
|
|
138
|
-
return actor.getLastEmittedValue();
|
|
139
|
-
});
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
### `useInterpret(machine, options?, observer?)`
|
|
143
|
-
|
|
144
|
-
A React hook that returns the `service` created from the `machine` with the `options`, if specified. It also sets up a subscription to the `service` with the `observer`, if provided.
|
|
145
|
-
|
|
146
|
-
_Since 1.3.0_
|
|
147
|
-
|
|
148
|
-
**Arguments**
|
|
149
|
-
|
|
150
|
-
- `machine` - An [XState machine](https://xstate.js.org/docs/guides/machines.html) or a function that lazily returns a machine.
|
|
151
|
-
- `options` (optional) - [Interpreter options](https://xstate.js.org/docs/guides/interpretation.html#options) and/or any of the following machine config options: `guards`, `actions`, `services`, `delays`, `immediate`, `context`, `state`.
|
|
152
|
-
- `observer` (optional) - an observer or listener that listens to state updates:
|
|
153
|
-
- an observer (e.g., `{ next: (state) => {/* ... */} }`)
|
|
154
|
-
- or a listener (e.g., `(state) => {/* ... */}`)
|
|
155
|
-
|
|
156
|
-
```js
|
|
157
|
-
import { useInterpret } from '@xstate/react';
|
|
158
|
-
import { someMachine } from '../path/to/someMachine';
|
|
159
|
-
|
|
160
|
-
const App = () => {
|
|
161
|
-
const service = useInterpret(someMachine);
|
|
162
|
-
|
|
163
|
-
// ...
|
|
164
|
-
};
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
With options + listener:
|
|
168
|
-
|
|
169
|
-
```js
|
|
170
|
-
// ...
|
|
171
|
-
|
|
172
|
-
const App = () => {
|
|
173
|
-
const service = useInterpret(
|
|
174
|
-
someMachine,
|
|
175
|
-
{
|
|
176
|
-
actions: {
|
|
177
|
-
/* ... */
|
|
178
|
-
}
|
|
179
|
-
},
|
|
180
|
-
(state) => {
|
|
181
|
-
// subscribes to state changes
|
|
182
|
-
console.log(state);
|
|
183
|
-
}
|
|
184
|
-
);
|
|
185
|
-
|
|
186
|
-
// ...
|
|
187
|
-
};
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
### `useSelector(actor, selector, compare?, getSnapshot?)`
|
|
191
|
-
|
|
192
|
-
A React hook that returns the selected value from the snapshot of an `actor`, such as a service. This hook will only cause a rerender if the selected value changes, as determined by the optional `compare` function.
|
|
193
|
-
|
|
194
|
-
_Since 1.3.0_
|
|
195
|
-
|
|
196
|
-
**Arguments**
|
|
197
|
-
|
|
198
|
-
- `actor` - a service or an actor-like object that contains `.send(...)` and `.subscribe(...)` methods.
|
|
199
|
-
- `selector` - a function that takes in an actor's "current state" (snapshot) as an argument and returns the desired selected value.
|
|
200
|
-
- `compare` (optional) - a function that determines if the current selected value is the same as the previous selected value.
|
|
201
|
-
- `getSnapshot` (optional) - a function that should return the latest emitted value from the `actor`.
|
|
202
|
-
- Defaults to attempting to get the `actor.state`, or returning `undefined` if that does not exist. Will automatically pull the state from services.
|
|
203
|
-
|
|
204
|
-
```js
|
|
205
|
-
import { useSelector } from '@xstate/react';
|
|
206
|
-
|
|
207
|
-
// tip: optimize selectors by defining them externally when possible
|
|
208
|
-
const selectCount = (state) => state.context.count;
|
|
209
|
-
|
|
210
|
-
const App = ({ service }) => {
|
|
211
|
-
const count = useSelector(service, selectCount);
|
|
212
|
-
|
|
213
|
-
// ...
|
|
214
|
-
};
|
|
215
|
-
```
|
|
216
|
-
|
|
217
|
-
With `compare` function:
|
|
218
|
-
|
|
219
|
-
```js
|
|
220
|
-
// ...
|
|
221
|
-
|
|
222
|
-
const selectUser = (state) => state.context.user;
|
|
223
|
-
const compareUser = (prevUser, nextUser) => prevUser.id === nextUser.id;
|
|
224
|
-
|
|
225
|
-
const App = ({ service }) => {
|
|
226
|
-
const user = useSelector(service, selectUser, compareUser);
|
|
227
|
-
|
|
228
|
-
// ...
|
|
229
|
-
};
|
|
230
|
-
```
|
|
231
|
-
|
|
232
|
-
With `useInterpret(...)`:
|
|
233
|
-
|
|
234
|
-
```js
|
|
235
|
-
import { useInterpret, useSelector } from '@xstate/react';
|
|
236
|
-
import { someMachine } from '../path/to/someMachine';
|
|
237
|
-
|
|
238
|
-
const selectCount = (state) => state.context.count;
|
|
239
|
-
|
|
240
|
-
const App = ({ service }) => {
|
|
241
|
-
const service = useInterpret(someMachine);
|
|
242
|
-
const count = useSelector(service, selectCount);
|
|
243
|
-
|
|
244
|
-
// ...
|
|
245
|
-
};
|
|
246
|
-
```
|
|
247
|
-
|
|
248
|
-
### `asEffect(action)`
|
|
249
|
-
|
|
250
|
-
Ensures that the `action` is executed as an effect in `useEffect`, rather than being immediately executed.
|
|
251
|
-
|
|
252
|
-
**Arguments**
|
|
253
|
-
|
|
254
|
-
- `action` - An action function (e.g., `(context, event) => { alert(context.message) })`)
|
|
255
|
-
|
|
256
|
-
**Returns** a special action function that wraps the original so that `useMachine` knows to execute it in `useEffect`.
|
|
257
|
-
|
|
258
|
-
**Example**
|
|
259
|
-
|
|
260
|
-
```jsx
|
|
261
|
-
const machine = createMachine({
|
|
262
|
-
initial: 'focused',
|
|
263
|
-
states: {
|
|
264
|
-
focused: {
|
|
265
|
-
entry: 'focus'
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
});
|
|
269
|
-
|
|
270
|
-
const Input = () => {
|
|
271
|
-
const inputRef = useRef(null);
|
|
272
|
-
const [state, send] = useMachine(machine, {
|
|
273
|
-
actions: {
|
|
274
|
-
focus: asEffect((context, event) => {
|
|
275
|
-
inputRef.current && inputRef.current.focus();
|
|
276
|
-
})
|
|
277
|
-
}
|
|
278
|
-
});
|
|
279
|
-
|
|
280
|
-
return <input ref={inputRef} />;
|
|
281
|
-
};
|
|
282
|
-
```
|
|
283
|
-
|
|
284
|
-
### `asLayoutEffect(action)`
|
|
285
|
-
|
|
286
|
-
Ensures that the `action` is executed as an effect in `useLayoutEffect`, rather than being immediately executed.
|
|
287
|
-
|
|
288
|
-
**Arguments**
|
|
289
|
-
|
|
290
|
-
- `action` - An action function (e.g., `(context, event) => { alert(context.message) })`)
|
|
291
|
-
|
|
292
|
-
**Returns** a special action function that wraps the original so that `useMachine` knows to execute it in `useLayoutEffect`.
|
|
293
|
-
|
|
294
|
-
### `useMachine(machine)` with `@xstate/fsm`
|
|
295
|
-
|
|
296
|
-
A [React hook](https://reactjs.org/hooks) that interprets the given finite state `machine` from [`@xstate/fsm`] and starts a service that runs for the lifetime of the component.
|
|
297
|
-
|
|
298
|
-
This special `useMachine` hook is imported from `@xstate/react/fsm`
|
|
299
|
-
|
|
300
|
-
**Arguments**
|
|
301
|
-
|
|
302
|
-
- `machine` - An [XState finite state machine (FSM)](https://xstate.js.org/docs/packages/xstate-fsm/).
|
|
303
|
-
- `options` - An optional `options` object.
|
|
304
|
-
|
|
305
|
-
**Returns** a tuple of `[state, send, service]`:
|
|
306
|
-
|
|
307
|
-
- `state` - Represents the current state of the machine as an `@xstate/fsm` `StateMachine.State` object.
|
|
308
|
-
- `send` - A function that sends events to the running service.
|
|
309
|
-
- `service` - The created `@xstate/fsm` service.
|
|
310
|
-
|
|
311
|
-
**Example**
|
|
312
|
-
|
|
313
|
-
```js
|
|
314
|
-
import { useEffect } from 'react';
|
|
315
|
-
import { useMachine } from '@xstate/react/fsm';
|
|
316
|
-
import { createMachine } from '@xstate/fsm';
|
|
317
|
-
|
|
318
|
-
const context = {
|
|
319
|
-
data: undefined
|
|
320
|
-
};
|
|
321
|
-
const fetchMachine = createMachine({
|
|
322
|
-
id: 'fetch',
|
|
323
|
-
initial: 'idle',
|
|
324
|
-
context,
|
|
325
|
-
states: {
|
|
326
|
-
idle: {
|
|
327
|
-
on: { FETCH: 'loading' }
|
|
328
|
-
},
|
|
329
|
-
loading: {
|
|
330
|
-
entry: ['load'],
|
|
331
|
-
on: {
|
|
332
|
-
RESOLVE: {
|
|
333
|
-
target: 'success',
|
|
334
|
-
actions: assign({
|
|
335
|
-
data: (context, event) => event.data
|
|
336
|
-
})
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
},
|
|
340
|
-
success: {}
|
|
341
|
-
}
|
|
342
|
-
});
|
|
343
|
-
|
|
344
|
-
const Fetcher = ({
|
|
345
|
-
onFetch = () => new Promise((res) => res('some data'))
|
|
346
|
-
}) => {
|
|
347
|
-
const [state, send] = useMachine(fetchMachine, {
|
|
348
|
-
actions: {
|
|
349
|
-
load: () => {
|
|
350
|
-
onFetch().then((res) => {
|
|
351
|
-
send({ type: 'RESOLVE', data: res });
|
|
352
|
-
});
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
});
|
|
356
|
-
|
|
357
|
-
switch (state.value) {
|
|
358
|
-
case 'idle':
|
|
359
|
-
return <button onClick={(_) => send('FETCH')}>Fetch</button>;
|
|
360
|
-
case 'loading':
|
|
361
|
-
return <div>Loading...</div>;
|
|
362
|
-
case 'success':
|
|
363
|
-
return (
|
|
364
|
-
<div>
|
|
365
|
-
Success! Data: <div data-testid="data">{state.context.data}</div>
|
|
366
|
-
</div>
|
|
367
|
-
);
|
|
368
|
-
default:
|
|
369
|
-
return null;
|
|
370
|
-
}
|
|
371
|
-
};
|
|
372
|
-
```
|
|
373
|
-
|
|
374
|
-
## Configuring Machines
|
|
375
|
-
|
|
376
|
-
Existing machines can be configured by passing the machine options as the 2nd argument of `useMachine(machine, options)`.
|
|
377
|
-
|
|
378
|
-
Example: the `'fetchData'` service and `'notifySuccess'` action are both configurable:
|
|
379
|
-
|
|
380
|
-
```js
|
|
381
|
-
const fetchMachine = createMachine({
|
|
382
|
-
id: 'fetch',
|
|
383
|
-
initial: 'idle',
|
|
384
|
-
context: {
|
|
385
|
-
data: undefined,
|
|
386
|
-
error: undefined
|
|
387
|
-
},
|
|
388
|
-
states: {
|
|
389
|
-
idle: {
|
|
390
|
-
on: { FETCH: 'loading' }
|
|
391
|
-
},
|
|
392
|
-
loading: {
|
|
393
|
-
invoke: {
|
|
394
|
-
src: 'fetchData',
|
|
395
|
-
onDone: {
|
|
396
|
-
target: 'success',
|
|
397
|
-
actions: assign({
|
|
398
|
-
data: (_, event) => event.data
|
|
399
|
-
})
|
|
400
|
-
},
|
|
401
|
-
onError: {
|
|
402
|
-
target: 'failure',
|
|
403
|
-
actions: assign({
|
|
404
|
-
error: (_, event) => event.data
|
|
405
|
-
})
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
},
|
|
409
|
-
success: {
|
|
410
|
-
entry: 'notifySuccess',
|
|
411
|
-
type: 'final'
|
|
412
|
-
},
|
|
413
|
-
failure: {
|
|
414
|
-
on: {
|
|
415
|
-
RETRY: 'loading'
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
});
|
|
420
|
-
|
|
421
|
-
const Fetcher = ({ onResolve }) => {
|
|
422
|
-
const [state, send] = useMachine(fetchMachine, {
|
|
423
|
-
actions: {
|
|
424
|
-
notifySuccess: (ctx) => onResolve(ctx.data)
|
|
425
|
-
},
|
|
426
|
-
services: {
|
|
427
|
-
fetchData: (_, e) =>
|
|
428
|
-
fetch(`some/api/${e.query}`).then((res) => res.json())
|
|
429
|
-
}
|
|
430
|
-
});
|
|
431
|
-
|
|
432
|
-
switch (state.value) {
|
|
433
|
-
case 'idle':
|
|
434
|
-
return (
|
|
435
|
-
<button onClick={() => send('FETCH', { query: 'something' })}>
|
|
436
|
-
Search for something
|
|
437
|
-
</button>
|
|
438
|
-
);
|
|
439
|
-
case 'loading':
|
|
440
|
-
return <div>Searching...</div>;
|
|
441
|
-
case 'success':
|
|
442
|
-
return <div>Success! Data: {state.context.data}</div>;
|
|
443
|
-
case 'failure':
|
|
444
|
-
return (
|
|
445
|
-
<>
|
|
446
|
-
<p>{state.context.error.message}</p>
|
|
447
|
-
<button onClick={() => send('RETRY')}>Retry</button>
|
|
448
|
-
</>
|
|
449
|
-
);
|
|
450
|
-
default:
|
|
451
|
-
return null;
|
|
452
|
-
}
|
|
453
|
-
};
|
|
454
|
-
```
|
|
455
|
-
|
|
456
|
-
## Matching States
|
|
457
|
-
|
|
458
|
-
When using [hierarchical](https://xstate.js.org/docs/guides/hierarchical.html) and [parallel](https://xstate.js.org/docs/guides/parallel.html) machines, the state values will be objects, not strings. In this case, it is best to use [`state.matches(...)`](https://xstate.js.org/docs/guides/states.html#state-methods-and-getters).
|
|
459
|
-
|
|
460
|
-
We can do this with `if/else if/else` blocks:
|
|
461
|
-
|
|
462
|
-
```js
|
|
463
|
-
// ...
|
|
464
|
-
if (state.matches('idle')) {
|
|
465
|
-
return /* ... */;
|
|
466
|
-
} else if (state.matches({ loading: 'user' })) {
|
|
467
|
-
return /* ... */;
|
|
468
|
-
} else if (state.matches({ loading: 'friends' })) {
|
|
469
|
-
return /* ... */;
|
|
470
|
-
} else {
|
|
471
|
-
return null;
|
|
472
|
-
}
|
|
473
|
-
```
|
|
474
|
-
|
|
475
|
-
We can also continue to use `switch`, but we must make an adjustment to our approach. By setting the expression of the `switch` to `true`, we can use [`state.matches(...)`](https://xstate.js.org/docs/guides/states.html#state-methods-and-getters) as a predicate in each `case`:
|
|
476
|
-
|
|
477
|
-
```js
|
|
478
|
-
switch (true) {
|
|
479
|
-
case state.matches('idle'):
|
|
480
|
-
return /* ... */;
|
|
481
|
-
case state.matches({ loading: 'user' }):
|
|
482
|
-
return /* ... */;
|
|
483
|
-
case state.matches({ loading: 'friends' }):
|
|
484
|
-
return /* ... */;
|
|
485
|
-
default:
|
|
486
|
-
return null;
|
|
487
|
-
}
|
|
488
|
-
```
|
|
489
|
-
|
|
490
|
-
A ternary statement can also be considered, especially within rendered JSX:
|
|
491
|
-
|
|
492
|
-
```jsx
|
|
493
|
-
const Loader = () => {
|
|
494
|
-
const [state, send] = useMachine(/* ... */);
|
|
495
|
-
|
|
496
|
-
return (
|
|
497
|
-
<div>
|
|
498
|
-
{state.matches('idle') ? (
|
|
499
|
-
<Loader.Idle />
|
|
500
|
-
) : state.matches({ loading: 'user' }) ? (
|
|
501
|
-
<Loader.LoadingUser />
|
|
502
|
-
) : state.matches({ loading: 'friends' }) ? (
|
|
503
|
-
<Loader.LoadingFriends />
|
|
504
|
-
) : null}
|
|
505
|
-
</div>
|
|
506
|
-
);
|
|
507
|
-
};
|
|
508
|
-
```
|
|
509
|
-
|
|
510
|
-
## Persisted and Rehydrated State
|
|
511
|
-
|
|
512
|
-
You can persist and rehydrate state with `useMachine(...)` via `options.state`:
|
|
513
|
-
|
|
514
|
-
```js
|
|
515
|
-
// ...
|
|
516
|
-
|
|
517
|
-
// Get the persisted state config object from somewhere, e.g. localStorage
|
|
518
|
-
const persistedState = JSON.parse(localStorage.getItem('some-persisted-state-key')) || someMachine.initialState;
|
|
519
|
-
|
|
520
|
-
const App = () => {
|
|
521
|
-
const [state, send] = useMachine(someMachine, {
|
|
522
|
-
state: persistedState // provide persisted state config object here
|
|
523
|
-
});
|
|
524
|
-
|
|
525
|
-
// state will initially be that persisted state, not the machine's initialState
|
|
526
|
-
|
|
527
|
-
return (/* ... */)
|
|
528
|
-
}
|
|
529
|
-
```
|
|
530
|
-
|
|
531
|
-
## Services
|
|
532
|
-
|
|
533
|
-
The `service` created in `useMachine(machine)` can be referenced as the third returned value:
|
|
534
|
-
|
|
535
|
-
```js
|
|
536
|
-
// vvvvvvv
|
|
537
|
-
const [state, send, service] = useMachine(someMachine);
|
|
538
|
-
```
|
|
539
|
-
|
|
540
|
-
You can subscribe to that service's state changes with the [`useEffect` hook](https://reactjs.org/docs/hooks-effect.html):
|
|
541
|
-
|
|
542
|
-
```js
|
|
543
|
-
// ...
|
|
544
|
-
|
|
545
|
-
useEffect(() => {
|
|
546
|
-
const subscription = service.subscribe((state) => {
|
|
547
|
-
// simple state logging
|
|
548
|
-
console.log(state);
|
|
549
|
-
});
|
|
550
|
-
|
|
551
|
-
return subscription.unsubscribe;
|
|
552
|
-
}, [service]); // note: service should never change
|
|
553
|
-
```
|
|
554
|
-
|
|
555
|
-
## Migration from 0.x
|
|
556
|
-
|
|
557
|
-
- For spawned actors created using `invoke` or `spawn(...)`, use the `useActor()` hook instead of `useService()`:
|
|
558
|
-
|
|
559
|
-
```diff
|
|
560
|
-
-import { useService } from '@xstate/react';
|
|
561
|
-
+import { useActor } from '@xstate/react';
|
|
562
|
-
|
|
563
|
-
-const [state, send] = useService(someActor);
|
|
564
|
-
+const [state, send] = useActor(someActor);
|
|
565
|
-
```
|
|
566
|
-
|
|
567
|
-
## Resources
|
|
568
|
-
|
|
569
|
-
[State Machines in React](https://gedd.ski/post/state-machines-in-react/)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
!function(t
|
|
1
|
+
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("react"),require("xstate"),require("xstate/lib/behaviors")):"function"==typeof define&&define.amd?define(["exports","react","xstate","xstate/lib/behaviors"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).XStateReact={},e.React,e.XState,e.behaviors)}(this,(function(e,t,n,r){"use strict";function u(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var i,o=u(t),c=function(){return(c=Object.assign||function(e){for(var t,n=1,r=arguments.length;n<r;n++)for(var u in t=arguments[n])Object.prototype.hasOwnProperty.call(t,u)&&(e[u]=t[u]);return e}).apply(this,arguments)};
|
|
2
2
|
/*! *****************************************************************************
|
|
3
3
|
Copyright (c) Microsoft Corporation.
|
|
4
4
|
|
|
@@ -12,4 +12,17 @@
|
|
|
12
12
|
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
13
13
|
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
14
14
|
PERFORMANCE OF THIS SOFTWARE.
|
|
15
|
-
***************************************************************************** */
|
|
15
|
+
***************************************************************************** */function a(e,t){var n="function"==typeof Symbol&&e[Symbol.iterator];if(!n)return e;var r,u,i=n.call(e),o=[];try{for(;(void 0===t||t-- >0)&&!(r=i.next()).done;)o.push(r.value)}catch(e){u={error:e}}finally{try{r&&!r.done&&(n=i.return)&&n.call(i)}finally{if(u)throw u.error}}return o}function f(e,t){for(var n=0,r=t.length,u=e.length;n<r;n++,u++)e[u]=t[n];return e}!function(e){e[e.Effect=1]="Effect",e[e.LayoutEffect=2]="LayoutEffect"}(i||(i={}));var s=t.useLayoutEffect;function l(e){var n=t.useRef();return n.current||(n.current={v:e()}),n.current.v}function v(e,t){var n,r,u=a([[],[]],2),i=u[0],o=u[1];try{for(var c=function(e){var t="function"==typeof Symbol&&Symbol.iterator,n=t&&e[t],r=0;if(n)return n.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&r>=e.length&&(e=void 0),{value:e&&e[r++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")}(e),f=c.next();!f.done;f=c.next()){var s=f.value;t(s)?i.push(s):o.push(s)}}catch(e){n={error:e}}finally{try{f&&!f.done&&(r=c.return)&&r.call(c)}finally{if(n)throw n.error}}return[i,o]}function b(e,t){(0,e.exec)(t.context,t._event.data,{action:e,state:t,_event:t._event})()}function p(e,r,u){void 0===r&&(r={});var o=l((function(){return"function"==typeof e?e():e})),p=r.context,d=r.guards,h=r.actions,y=r.activities,g=r.services,O=r.delays,j=r.state,m=function(e,t){var n={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&t.indexOf(r)<0&&(n[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols){var u=0;for(r=Object.getOwnPropertySymbols(e);u<r.length;u++)t.indexOf(r[u])<0&&Object.prototype.propertyIsEnumerable.call(e,r[u])&&(n[r[u]]=e[r[u]])}return n}(r,["context","guards","actions","activities","services","delays","state"]),x=l((function(){var e={context:p,guards:d,actions:h,activities:y,services:g,delays:O},t=o.withConfig(e,(function(){return c(c({},o.context),p)}));return n.interpret(t,c({deferEvents:!0},m))}));return s((function(){var e;return u&&(e=x.subscribe(function(e,t,n){if("object"==typeof e)return e;var r=function(){};return{next:e,error:t||r,complete:n||r}}(u))),function(){null==e||e.unsubscribe()}}),[u]),s((function(){return x.start(j?n.State.create(j):void 0),function(){x.stop()}}),[]),s((function(){Object.assign(x.machine.options.actions,h),Object.assign(x.machine.options.guards,d),Object.assign(x.machine.options.activities,y),Object.assign(x.machine.options.services,g),Object.assign(x.machine.options.delays,O)}),[h,d,y,g,O]),function(e){var n=t.useRef([]),r=t.useRef([]);s((function(){var t=e.subscribe((function(e){var t,u;if(e.actions.length){var o=a(v(e.actions.filter((function(e){return"function"==typeof e.exec&&"__effect"in e.exec})),(function(e){return e.exec.__effect===i.Effect})),2),c=o[0],s=o[1];(t=n.current).push.apply(t,f([],a(c.map((function(t){return[t,e]}))))),(u=r.current).push.apply(u,f([],a(s.map((function(t){return[t,e]})))))}}));return function(){t.unsubscribe()}}),[]),s((function(){for(;r.current.length;){var e=a(r.current.shift(),2);b(e[0],e[1])}})),t.useEffect((function(){for(;n.current.length;){var e=a(n.current.shift(),2);b(e[0],e[1])}}))}(x),x}function d(e,t){var n=function(){for(var t=[],n=0;n<arguments.length;n++)t[n]=arguments[n];return function(){return e.apply(void 0,f([],a(t)))}};return Object.defineProperties(n,{name:{value:"effect:"+e.name},__effect:{value:t}}),n}function h(e){return"state"in e}function y(e){return"deferred"in e}var g=function(){};function O(e){return"getSnapshot"in e?e.getSnapshot():h(e)?e.state:void 0}function j(e,n){void 0===n&&(n=O);var r=t.useRef(e),u=t.useRef([]),i=a(t.useState((function(){return n(e)})),2),o=i[0],c=i[1],f=l((function(){return function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];var n=e[0],i=r.current;y(i)&&i.deferred?u.current.push(n):i.send(n)}}));return s((function(){r.current=e,c(n(e));for(var t=e.subscribe({next:function(e){return c(e)},error:g,complete:g});u.current.length>0;){var i=u.current.shift();e.send(i)}return function(){t.unsubscribe()}}),[e]),[o,f]}function m(e){var t={exports:{}};return e(t,t.exports),t.exports
|
|
16
|
+
/*
|
|
17
|
+
object-assign
|
|
18
|
+
(c) Sindre Sorhus
|
|
19
|
+
@license MIT
|
|
20
|
+
*/}var x=Object.getOwnPropertySymbols,S=Object.prototype.hasOwnProperty,w=Object.prototype.propertyIsEnumerable;function E(e){if(null==e)throw new TypeError("Object.assign cannot be called with null or undefined");return Object(e)}var _=function(){try{if(!Object.assign)return!1;var e=new String("abc");if(e[5]="de","5"===Object.getOwnPropertyNames(e)[0])return!1;for(var t={},n=0;n<10;n++)t["_"+String.fromCharCode(n)]=n;if("0123456789"!==Object.getOwnPropertyNames(t).map((function(e){return t[e]})).join(""))return!1;var r={};return"abcdefghijklmnopqrst".split("").forEach((function(e){r[e]=e})),"abcdefghijklmnopqrst"===Object.keys(Object.assign({},r)).join("")}catch(e){return!1}}()?Object.assign:function(e,t){for(var n,r,u=E(e),i=1;i<arguments.length;i++){for(var o in n=Object(arguments[i]))S.call(n,o)&&(u[o]=n[o]);if(x){r=x(n);for(var c=0;c<r.length;c++)w.call(n,r[c])&&(u[r[c]]=n[r[c]])}}return u},C={useSubscription:function(e){var t=e.getCurrentValue,n=e.subscribe,r=o.default.useState((function(){return{getCurrentValue:t,subscribe:n,value:t()}}));e=r[0];var u=r[1];return r=e.value,e.getCurrentValue===t&&e.subscribe===n||(r=t(),u({getCurrentValue:t,subscribe:n,value:r})),o.default.useDebugValue(r),o.default.useEffect((function(){function e(){if(!r){var e=t();u((function(r){return r.getCurrentValue!==t||r.subscribe!==n||r.value===e?r:_({},r,{value:e})}))}}var r=!1,i=n(e);return e(),function(){r=!0,i()}}),[t,n]),r}},P=(m((function(e,t){})),m((function(e){e.exports=C})));
|
|
21
|
+
/** @license React vundefined
|
|
22
|
+
* use-subscription.production.min.js
|
|
23
|
+
*
|
|
24
|
+
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
25
|
+
*
|
|
26
|
+
* This source code is licensed under the MIT license found in the
|
|
27
|
+
* LICENSE file in the root directory of this source tree.
|
|
28
|
+
*/var V=function(e,t){return e===t},R=function(e){return"state"in(n=e)&&"machine"in n?0!==("status"in(t=e)?t.status:t._status)?t.state:t.machine.initialState:h(e)?e.state:void 0;var t,n};e.asEffect=function(e){return d(e,i.Effect)},e.asLayoutEffect=function(e){return d(e,i.LayoutEffect)},e.useActor=j,e.useInterpret=p,e.useMachine=function(e,r){void 0===r&&(r={});var u=t.useCallback((function(e){var t=void 0===e.changed&&Object.keys(e.children).length;(e.changed||t)&&f(e)}),[]),i=p(e,r,u),o=a(t.useState((function(){var e=i.machine.initialState;return r.state?n.State.create(r.state):e})),2),c=o[0],f=o[1];return[c,i.send,i]},e.useSelector=function(e,n,r,u){void 0===r&&(r=V),void 0===u&&(u=R);var i=t.useRef(n),o=t.useMemo((function(){var t,o=u(e),c=n(o);return{getSnapshot:function(){return o},getCurrentValue:function(){return c},setCurrentValue:function(e){c=e,null==t||t()},subscribe:function(n){t=n;var u=e.subscribe((function(e){o=e;var t=i.current(e);r(c,t)||(c=t,n())}));return function(){u.unsubscribe()}}}}),[e]),c=P.useSubscription(o);if(i.current!==n){var a=n(o.getSnapshot());r(c,a)||(c=a)}return s((function(){i.current=n,o.setCurrentValue(c)})),c},e.useService=function(e){return[a(j(e),1)[0],e.send]},e.useSpawn=function(e){return l((function(){return r.spawnBehavior(e)}))},Object.defineProperty(e,"__esModule",{value:!0})}));
|
package/es/useInterpret.js
CHANGED
|
@@ -62,7 +62,7 @@ export function useInterpret(getMachine, options, observerOrListener) {
|
|
|
62
62
|
if (process.env.NODE_ENV !== 'production' &&
|
|
63
63
|
typeof getMachine !== 'function') {
|
|
64
64
|
var _a = __read(useState(machine), 1), initialMachine = _a[0];
|
|
65
|
-
if (
|
|
65
|
+
if (getMachine !== initialMachine) {
|
|
66
66
|
console.warn('Machine given to `useMachine` has changed between renders. This is not supported and might lead to unexpected results.\n' +
|
|
67
67
|
'Please make sure that you pass the same Machine as argument each time.');
|
|
68
68
|
}
|
package/es/useSelector.js
CHANGED
|
@@ -1,20 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
var i = m.call(o), r, ar = [], e;
|
|
5
|
-
try {
|
|
6
|
-
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
|
|
7
|
-
}
|
|
8
|
-
catch (error) { e = { error: error }; }
|
|
9
|
-
finally {
|
|
10
|
-
try {
|
|
11
|
-
if (r && !r.done && (m = i["return"])) m.call(i);
|
|
12
|
-
}
|
|
13
|
-
finally { if (e) throw e.error; }
|
|
14
|
-
}
|
|
15
|
-
return ar;
|
|
16
|
-
};
|
|
17
|
-
import { useEffect, useRef, useState } from 'react';
|
|
1
|
+
import { useMemo, useRef } from 'react';
|
|
2
|
+
import { useSubscription } from 'use-subscription';
|
|
3
|
+
import useIsomorphicLayoutEffect from 'use-isomorphic-layout-effect';
|
|
18
4
|
import { isActorWithState } from './useActor';
|
|
19
5
|
import { getServiceSnapshot } from './useService';
|
|
20
6
|
function isService(actor) {
|
|
@@ -31,22 +17,49 @@ var defaultGetSnapshot = function (a) {
|
|
|
31
17
|
export function useSelector(actor, selector, compare, getSnapshot) {
|
|
32
18
|
if (compare === void 0) { compare = defaultCompare; }
|
|
33
19
|
if (getSnapshot === void 0) { getSnapshot = defaultGetSnapshot; }
|
|
34
|
-
var
|
|
35
|
-
var
|
|
36
|
-
|
|
37
|
-
var
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
20
|
+
var latestSelectorRef = useRef(selector);
|
|
21
|
+
var subscription = useMemo(function () {
|
|
22
|
+
var snapshot = getSnapshot(actor);
|
|
23
|
+
var current = selector(snapshot);
|
|
24
|
+
var notifySubscriber;
|
|
25
|
+
return {
|
|
26
|
+
getSnapshot: function () { return snapshot; },
|
|
27
|
+
getCurrentValue: function () { return current; },
|
|
28
|
+
setCurrentValue: function (newCurrent) {
|
|
29
|
+
current = newCurrent;
|
|
30
|
+
notifySubscriber === null || notifySubscriber === void 0 ? void 0 : notifySubscriber();
|
|
31
|
+
},
|
|
32
|
+
subscribe: function (callback) {
|
|
33
|
+
notifySubscriber = callback;
|
|
34
|
+
var sub = actor.subscribe(function (emitted) {
|
|
35
|
+
snapshot = emitted;
|
|
36
|
+
var next = latestSelectorRef.current(emitted);
|
|
37
|
+
if (!compare(current, next)) {
|
|
38
|
+
current = next;
|
|
39
|
+
callback();
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
return function () {
|
|
43
|
+
sub.unsubscribe();
|
|
44
|
+
};
|
|
41
45
|
}
|
|
42
46
|
};
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
47
|
+
// intentionally omit `getSnapshot` and `compare`
|
|
48
|
+
// - `getSnapshot`: it is only supposed to read the "initial" snapshot of an actor
|
|
49
|
+
// - `compare`: is really supposed to be idempotent and the same throughout the lifetime of this hook (the same assumption is made in React Redux v7)
|
|
50
|
+
}, [actor]);
|
|
51
|
+
var currentSelected = useSubscription(subscription);
|
|
52
|
+
if (latestSelectorRef.current !== selector) {
|
|
53
|
+
var selected = selector(subscription.getSnapshot());
|
|
54
|
+
if (!compare(currentSelected, selected)) {
|
|
55
|
+
currentSelected = selected;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
useIsomorphicLayoutEffect(function () {
|
|
59
|
+
latestSelectorRef.current = selector;
|
|
60
|
+
// required so we don't cause a rerender by setting state (this could create infinite rerendering loop with inline selectors)
|
|
61
|
+
// at the same time we need to update the value within the subscription so new emits can compare against what has been returned to the user as current value
|
|
62
|
+
subscription.setCurrentValue(currentSelected);
|
|
63
|
+
});
|
|
64
|
+
return currentSelected;
|
|
52
65
|
}
|
package/lib/useInterpret.js
CHANGED
|
@@ -65,7 +65,7 @@ function useInterpret(getMachine, options, observerOrListener) {
|
|
|
65
65
|
if (process.env.NODE_ENV !== 'production' &&
|
|
66
66
|
typeof getMachine !== 'function') {
|
|
67
67
|
var _a = __read(react_1.useState(machine), 1), initialMachine = _a[0];
|
|
68
|
-
if (
|
|
68
|
+
if (getMachine !== initialMachine) {
|
|
69
69
|
console.warn('Machine given to `useMachine` has changed between renders. This is not supported and might lead to unexpected results.\n' +
|
|
70
70
|
'Please make sure that you pass the same Machine as argument each time.');
|
|
71
71
|
}
|
package/lib/useSelector.js
CHANGED
|
@@ -1,23 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __read = (this && this.__read) || function (o, n) {
|
|
3
|
-
var m = typeof Symbol === "function" && o[Symbol.iterator];
|
|
4
|
-
if (!m) return o;
|
|
5
|
-
var i = m.call(o), r, ar = [], e;
|
|
6
|
-
try {
|
|
7
|
-
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
|
|
8
|
-
}
|
|
9
|
-
catch (error) { e = { error: error }; }
|
|
10
|
-
finally {
|
|
11
|
-
try {
|
|
12
|
-
if (r && !r.done && (m = i["return"])) m.call(i);
|
|
13
|
-
}
|
|
14
|
-
finally { if (e) throw e.error; }
|
|
15
|
-
}
|
|
16
|
-
return ar;
|
|
17
|
-
};
|
|
18
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
3
|
exports.useSelector = void 0;
|
|
20
4
|
var react_1 = require("react");
|
|
5
|
+
var use_subscription_1 = require("use-subscription");
|
|
6
|
+
var use_isomorphic_layout_effect_1 = require("use-isomorphic-layout-effect");
|
|
21
7
|
var useActor_1 = require("./useActor");
|
|
22
8
|
var useService_1 = require("./useService");
|
|
23
9
|
function isService(actor) {
|
|
@@ -34,23 +20,50 @@ var defaultGetSnapshot = function (a) {
|
|
|
34
20
|
function useSelector(actor, selector, compare, getSnapshot) {
|
|
35
21
|
if (compare === void 0) { compare = defaultCompare; }
|
|
36
22
|
if (getSnapshot === void 0) { getSnapshot = defaultGetSnapshot; }
|
|
37
|
-
var
|
|
38
|
-
var
|
|
39
|
-
|
|
40
|
-
var
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
23
|
+
var latestSelectorRef = react_1.useRef(selector);
|
|
24
|
+
var subscription = react_1.useMemo(function () {
|
|
25
|
+
var snapshot = getSnapshot(actor);
|
|
26
|
+
var current = selector(snapshot);
|
|
27
|
+
var notifySubscriber;
|
|
28
|
+
return {
|
|
29
|
+
getSnapshot: function () { return snapshot; },
|
|
30
|
+
getCurrentValue: function () { return current; },
|
|
31
|
+
setCurrentValue: function (newCurrent) {
|
|
32
|
+
current = newCurrent;
|
|
33
|
+
notifySubscriber === null || notifySubscriber === void 0 ? void 0 : notifySubscriber();
|
|
34
|
+
},
|
|
35
|
+
subscribe: function (callback) {
|
|
36
|
+
notifySubscriber = callback;
|
|
37
|
+
var sub = actor.subscribe(function (emitted) {
|
|
38
|
+
snapshot = emitted;
|
|
39
|
+
var next = latestSelectorRef.current(emitted);
|
|
40
|
+
if (!compare(current, next)) {
|
|
41
|
+
current = next;
|
|
42
|
+
callback();
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
return function () {
|
|
46
|
+
sub.unsubscribe();
|
|
47
|
+
};
|
|
44
48
|
}
|
|
45
49
|
};
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
50
|
+
// intentionally omit `getSnapshot` and `compare`
|
|
51
|
+
// - `getSnapshot`: it is only supposed to read the "initial" snapshot of an actor
|
|
52
|
+
// - `compare`: is really supposed to be idempotent and the same throughout the lifetime of this hook (the same assumption is made in React Redux v7)
|
|
53
|
+
}, [actor]);
|
|
54
|
+
var currentSelected = use_subscription_1.useSubscription(subscription);
|
|
55
|
+
if (latestSelectorRef.current !== selector) {
|
|
56
|
+
var selected = selector(subscription.getSnapshot());
|
|
57
|
+
if (!compare(currentSelected, selected)) {
|
|
58
|
+
currentSelected = selected;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
use_isomorphic_layout_effect_1.default(function () {
|
|
62
|
+
latestSelectorRef.current = selector;
|
|
63
|
+
// required so we don't cause a rerender by setting state (this could create infinite rerendering loop with inline selectors)
|
|
64
|
+
// at the same time we need to update the value within the subscription so new emits can compare against what has been returned to the user as current value
|
|
65
|
+
subscription.setCurrentValue(currentSelected);
|
|
66
|
+
});
|
|
67
|
+
return currentSelected;
|
|
55
68
|
}
|
|
56
69
|
exports.useSelector = useSelector;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xstate/react",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.2",
|
|
4
4
|
"description": "XState tools for React",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"state",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"clean": "rm -rf dist lib tsconfig.tsbuildinfo",
|
|
40
40
|
"build": "tsc && tsc --outDir es --module es2015 && rollup -c",
|
|
41
41
|
"test": "jest",
|
|
42
|
-
"prepublish": "npm run build
|
|
42
|
+
"prepublish": "npm run build"
|
|
43
43
|
},
|
|
44
44
|
"bugs": {
|
|
45
45
|
"url": "https://github.com/davidkpiano/xstate/issues"
|