@xstate/react 1.5.0 → 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 CHANGED
@@ -1,5 +1,107 @@
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
+
42
+ ## 1.6.1
43
+
44
+ ### Patch Changes
45
+
46
+ - [#2587](https://github.com/statelyai/xstate/pull/2587) [`5aaa8445c`](https://github.com/statelyai/xstate/commit/5aaa8445c0041c6e9c47285c18e8b71cb2d805a7) Thanks [@Andarist](https://github.com/Andarist)! - Fixed an issue with implementations provided outside of React being wiped out and unusable.
47
+
48
+ ## 1.6.0
49
+
50
+ ### Minor Changes
51
+
52
+ - [`4b4872ca`](https://github.com/statelyai/xstate/commit/4b4872cafd63f825f3918c6eb6fa84642d45e3e0) [#2241](https://github.com/statelyai/xstate/pull/2241) Thanks [@mattpocock](https://github.com/mattpocock)! - Changed the behaviour of guards, delays and activities when declared as options in `useMachine`/`useInterpret`.
53
+
54
+ Previously, guards could not reference external props, because they would not be updated when the props changed. For instance:
55
+
56
+ ```tsx
57
+ const Modal = props => {
58
+ useMachine(modalMachine, {
59
+ guards: {
60
+ isModalOpen: () => props.isOpen
61
+ }
62
+ });
63
+ };
64
+ ```
65
+
66
+ When the component is created, `props.isOpen` would be checked and evaluated to the initial value. But if the guard is evaluated at any other time, it will not respond to the props' changed value.
67
+
68
+ This is not true of actions/services. This will work as expected:
69
+
70
+ ```tsx
71
+ const Modal = props => {
72
+ useMachine(modalMachine, {
73
+ actions: {
74
+ consoleLogModalOpen: () => {
75
+ console.log(props.isOpen);
76
+ }
77
+ }
78
+ });
79
+ };
80
+ ```
81
+
82
+ This change brings guards and delays into line with actions and services.
83
+
84
+ ⚠️ **NOTE:** Whenever possible, use data from within `context` rather than external data in your guards and delays.
85
+
86
+ ### Patch Changes
87
+
88
+ - [`fe3e859f`](https://github.com/statelyai/xstate/commit/fe3e859f5c53813307bacad915bebc8d1f3a982c) [#2522](https://github.com/statelyai/xstate/pull/2522) Thanks [@farskid](https://github.com/farskid), [@Andarist](https://github.com/Andarist)! - Fixed an issue with actors not being spawned correctly by `useMachine` and `useInterpret` when they were defined a lazily evaluated context, like for example here:
89
+
90
+ ```js
91
+ createMachine({
92
+ // lazy context
93
+ context: () => ({
94
+ ref: spawn(() => {})
95
+ })
96
+ });
97
+ ```
98
+
99
+ ## 1.5.1
100
+
101
+ ### Patch Changes
102
+
103
+ - [`453acacb`](https://github.com/statelyai/xstate/commit/453acacbec364531a2851f183c3ab446d7db0e84) [#2389](https://github.com/statelyai/xstate/pull/2389) Thanks [@davidkpiano](https://github.com/davidkpiano)! - An internal issue where the `spawnBehavior` import for the `useSpawn(...)` hook was broken internally has been fixed.
104
+
3
105
  ## 1.5.0
4
106
 
5
107
  ### Minor Changes
package/README.md CHANGED
@@ -1,8 +1,11 @@
1
1
  # @xstate/react
2
2
 
3
- [[toc]]
3
+ This package contains utilities for using [XState](https://github.com/statelyai/xstate) with [React](https://github.com/facebook/react/).
4
4
 
5
- ## Quick Start
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,505 +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
- :::
102
-
103
- A [React hook](https://reactjs.org/hooks) that subscribes to state changes from an existing [service](https://xstate.js.org/docs/guides/interpretation.html).
104
-
105
- **Arguments**
106
-
107
- - `service` - An [XState service](https://xstate.js.org/docs/guides/interpretation.html).
108
-
109
- **Returns** a tuple of `[state, send]`:
110
-
111
- - `state` - Represents the current state of the service as an XState `State` object.
112
- - `send` - A function that sends events to the running service.
113
-
114
- ### `useActor(actor, getSnapshot?)`
115
-
116
- A [React hook](https://reactjs.org/hooks) that subscribes to emitted changes from an existing [actor](https://xstate.js.org/docs/guides/actors.html).
117
-
118
- **Arguments**
119
-
120
- - `actor` - an actor-like object that contains `.send(...)` and `.subscribe(...)` methods.
121
- - `getSnapshot` - a function that should return the latest emitted value from the `actor`.
122
- - Defaults to attempting to get the `actor.state`, or returning `undefined` if that does not exist.
123
-
124
- ```js
125
- const [state, send] = useActor(someSpawnedActor);
126
-
127
- // with custom actors
128
- const [state, send] = useActor(customActor, (actor) => {
129
- // implementation-specific pseudocode example:
130
- return actor.getLastEmittedValue();
131
- });
132
- ```
133
-
134
- ### `useInterpret(machine, options?, observer?)`
135
-
136
- 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.
137
-
138
- _Since 1.3.0_
139
-
140
- **Arguments**
141
-
142
- - `machine` - An [XState machine](https://xstate.js.org/docs/guides/machines.html) or a function that lazily returns a machine.
143
- - `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`.
144
- - `observer` (optional) - an observer or listener that listens to state updates:
145
- - an observer (e.g., `{ next: (state) => {/* ... */} }`)
146
- - or a listener (e.g., `(state) => {/* ... */}`)
147
-
148
- ```js
149
- import { useInterpret } from '@xstate/react';
150
- import { someMachine } from '../path/to/someMachine';
151
-
152
- const App = () => {
153
- const service = useInterpret(someMachine);
154
-
155
- // ...
156
- };
157
- ```
158
-
159
- With options + listener:
160
-
161
- ```js
162
- // ...
163
-
164
- const App = () => {
165
- const service = useInterpret(
166
- someMachine,
167
- {
168
- actions: {
169
- /* ... */
170
- }
171
- },
172
- (state) => {
173
- // subscribes to state changes
174
- console.log(state);
175
- }
176
- );
177
-
178
- // ...
179
- };
180
- ```
181
-
182
- ### `useSelector(actor, selector, compare?, getSnapshot?)`
183
-
184
- 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.
185
-
186
- _Since 1.3.0_
187
-
188
- **Arguments**
189
-
190
- - `actor` - a service or an actor-like object that contains `.send(...)` and `.subscribe(...)` methods.
191
- - `selector` - a function that takes in an actor's "current state" (snapshot) as an argument and returns the desired selected value.
192
- - `compare` (optional) - a function that determines if the current selected value is the same as the previous selected value.
193
- - `getSnapshot` (optional) - a function that should return the latest emitted value from the `actor`.
194
- - Defaults to attempting to get the `actor.state`, or returning `undefined` if that does not exist. Will automatically pull the state from services.
195
-
196
- ```js
197
- import { useSelector } from '@xstate/react';
198
-
199
- // tip: optimize selectors by defining them externally when possible
200
- const selectCount = (state) => state.context.count;
201
-
202
- const App = ({ service }) => {
203
- const count = useSelector(service, selectCount);
204
-
205
- // ...
206
- };
207
- ```
208
-
209
- With `compare` function:
210
-
211
- ```js
212
- // ...
213
-
214
- const selectUser = (state) => state.context.user;
215
- const compareUser = (prevUser, nextUser) => prevUser.id === nextUser.id;
216
-
217
- const App = ({ service }) => {
218
- const user = useSelector(service, selectUser, compareUser);
219
-
220
- // ...
221
- };
222
- ```
223
-
224
- With `useInterpret(...)`:
225
-
226
- ```js
227
- import { useInterpret, useSelector } from '@xstate/react';
228
- import { someMachine } from '../path/to/someMachine';
229
-
230
- const selectCount = (state) => state.context.count;
231
-
232
- const App = ({ service }) => {
233
- const service = useInterpret(someMachine);
234
- const count = useSelector(service, selectCount);
235
-
236
- // ...
237
- };
238
- ```
239
-
240
- ### `asEffect(action)`
241
-
242
- Ensures that the `action` is executed as an effect in `useEffect`, rather than being immediately executed.
243
-
244
- **Arguments**
245
-
246
- - `action` - An action function (e.g., `(context, event) => { alert(context.message) })`)
247
-
248
- **Returns** a special action function that wraps the original so that `useMachine` knows to execute it in `useEffect`.
249
-
250
- **Example**
251
-
252
- ```jsx
253
- const machine = createMachine({
254
- initial: 'focused',
255
- states: {
256
- focused: {
257
- entry: 'focus'
258
- }
259
- }
260
- });
261
-
262
- const Input = () => {
263
- const inputRef = useRef(null);
264
- const [state, send] = useMachine(machine, {
265
- actions: {
266
- focus: asEffect((context, event) => {
267
- inputRef.current && inputRef.current.focus();
268
- })
269
- }
270
- });
271
-
272
- return <input ref={inputRef} />;
273
- };
274
- ```
275
-
276
- ### `asLayoutEffect(action)`
277
-
278
- Ensures that the `action` is executed as an effect in `useLayoutEffect`, rather than being immediately executed.
279
-
280
- **Arguments**
281
-
282
- - `action` - An action function (e.g., `(context, event) => { alert(context.message) })`)
283
-
284
- **Returns** a special action function that wraps the original so that `useMachine` knows to execute it in `useLayoutEffect`.
285
-
286
- ### `useMachine(machine)` with `@xstate/fsm`
287
-
288
- 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.
289
-
290
- This special `useMachine` hook is imported from `@xstate/react/fsm`
291
-
292
- **Arguments**
293
-
294
- - `machine` - An [XState finite state machine (FSM)](https://xstate.js.org/docs/packages/xstate-fsm/).
295
- - `options` - An optional `options` object.
296
-
297
- **Returns** a tuple of `[state, send, service]`:
298
-
299
- - `state` - Represents the current state of the machine as an `@xstate/fsm` `StateMachine.State` object.
300
- - `send` - A function that sends events to the running service.
301
- - `service` - The created `@xstate/fsm` service.
302
-
303
- **Example**
304
-
305
- ```js
306
- import { useEffect } from 'react';
307
- import { useMachine } from '@xstate/react/fsm';
308
- import { createMachine } from '@xstate/fsm';
309
-
310
- const context = {
311
- data: undefined
312
- };
313
- const fetchMachine = createMachine({
314
- id: 'fetch',
315
- initial: 'idle',
316
- context,
317
- states: {
318
- idle: {
319
- on: { FETCH: 'loading' }
320
- },
321
- loading: {
322
- entry: ['load'],
323
- on: {
324
- RESOLVE: {
325
- target: 'success',
326
- actions: assign({
327
- data: (context, event) => event.data
328
- })
329
- }
330
- }
331
- },
332
- success: {}
333
- }
334
- });
335
-
336
- const Fetcher = ({
337
- onFetch = () => new Promise((res) => res('some data'))
338
- }) => {
339
- const [state, send] = useMachine(fetchMachine, {
340
- actions: {
341
- load: () => {
342
- onFetch().then((res) => {
343
- send({ type: 'RESOLVE', data: res });
344
- });
345
- }
346
- }
347
- });
348
-
349
- switch (state.value) {
350
- case 'idle':
351
- return <button onClick={(_) => send('FETCH')}>Fetch</button>;
352
- case 'loading':
353
- return <div>Loading...</div>;
354
- case 'success':
355
- return (
356
- <div>
357
- Success! Data: <div data-testid="data">{state.context.data}</div>
358
- </div>
359
- );
360
- default:
361
- return null;
362
- }
363
- };
364
- ```
365
-
366
- ## Configuring Machines
367
-
368
- Existing machines can be configured by passing the machine options as the 2nd argument of `useMachine(machine, options)`.
369
-
370
- Example: the `'fetchData'` service and `'notifySuccess'` action are both configurable:
371
-
372
- ```js
373
- const fetchMachine = createMachine({
374
- id: 'fetch',
375
- initial: 'idle',
376
- context: {
377
- data: undefined,
378
- error: undefined
379
- },
380
- states: {
381
- idle: {
382
- on: { FETCH: 'loading' }
383
- },
384
- loading: {
385
- invoke: {
386
- src: 'fetchData',
387
- onDone: {
388
- target: 'success',
389
- actions: assign({
390
- data: (_, event) => event.data
391
- })
392
- },
393
- onError: {
394
- target: 'failure',
395
- actions: assign({
396
- error: (_, event) => event.data
397
- })
398
- }
399
- }
400
- },
401
- success: {
402
- entry: 'notifySuccess',
403
- type: 'final'
404
- },
405
- failure: {
406
- on: {
407
- RETRY: 'loading'
408
- }
409
- }
410
- }
411
- });
412
-
413
- const Fetcher = ({ onResolve }) => {
414
- const [state, send] = useMachine(fetchMachine, {
415
- actions: {
416
- notifySuccess: (ctx) => onResolve(ctx.data)
417
- },
418
- services: {
419
- fetchData: (_, e) =>
420
- fetch(`some/api/${e.query}`).then((res) => res.json())
421
- }
422
- });
423
-
424
- switch (state.value) {
425
- case 'idle':
426
- return (
427
- <button onClick={() => send('FETCH', { query: 'something' })}>
428
- Search for something
429
- </button>
430
- );
431
- case 'loading':
432
- return <div>Searching...</div>;
433
- case 'success':
434
- return <div>Success! Data: {state.context.data}</div>;
435
- case 'failure':
436
- return (
437
- <>
438
- <p>{state.context.error.message}</p>
439
- <button onClick={() => send('RETRY')}>Retry</button>
440
- </>
441
- );
442
- default:
443
- return null;
444
- }
445
- };
446
- ```
447
-
448
- ## Matching States
449
-
450
- 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).
451
-
452
- We can do this with `if/else if/else` blocks:
453
-
454
- ```js
455
- // ...
456
- if (state.matches('idle')) {
457
- return /* ... */;
458
- } else if (state.matches({ loading: 'user' })) {
459
- return /* ... */;
460
- } else if (state.matches({ loading: 'friends' })) {
461
- return /* ... */;
462
- } else {
463
- return null;
464
- }
465
- ```
466
-
467
- 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`:
468
-
469
- ```js
470
- switch (true) {
471
- case state.matches('idle'):
472
- return /* ... */;
473
- case state.matches({ loading: 'user' }):
474
- return /* ... */;
475
- case state.matches({ loading: 'friends' }):
476
- return /* ... */;
477
- default:
478
- return null;
479
- }
480
- ```
481
-
482
- A ternary statement can also be considered, especially within rendered JSX:
483
-
484
- ```jsx
485
- const Loader = () => {
486
- const [state, send] = useMachine(/* ... */);
487
-
488
- return (
489
- <div>
490
- {state.matches('idle') ? (
491
- <Loader.Idle />
492
- ) : state.matches({ loading: 'user' }) ? (
493
- <Loader.LoadingUser />
494
- ) : state.matches({ loading: 'friends' }) ? (
495
- <Loader.LoadingFriends />
496
- ) : null}
497
- </div>
498
- );
499
- };
500
- ```
501
-
502
- ## Persisted and Rehydrated State
503
-
504
- You can persist and rehydrate state with `useMachine(...)` via `options.state`:
505
-
506
- ```js
507
- // ...
508
-
509
- // Get the persisted state config object from somewhere, e.g. localStorage
510
- const persistedState = JSON.parse(localStorage.getItem('some-persisted-state-key')) || someMachine.initialState;
511
-
512
- const App = () => {
513
- const [state, send] = useMachine(someMachine, {
514
- state: persistedState // provide persisted state config object here
515
- });
516
-
517
- // state will initially be that persisted state, not the machine's initialState
518
-
519
- return (/* ... */)
520
- }
521
- ```
522
-
523
- ## Services
524
-
525
- The `service` created in `useMachine(machine)` can be referenced as the third returned value:
526
-
527
- ```js
528
- // vvvvvvv
529
- const [state, send, service] = useMachine(someMachine);
530
- ```
531
-
532
- You can subscribe to that service's state changes with the [`useEffect` hook](https://reactjs.org/docs/hooks-effect.html):
533
-
534
- ```js
535
- // ...
536
-
537
- useEffect(() => {
538
- const subscription = service.subscribe((state) => {
539
- // simple state logging
540
- console.log(state);
541
- });
542
-
543
- return subscription.unsubscribe;
544
- }, [service]); // note: service should never change
545
- ```
546
-
547
- ## Migration from 0.x
548
-
549
- - For spawned actors created using `invoke` or `spawn(...)`, use the `useActor()` hook instead of `useService()`:
550
-
551
- ```diff
552
- -import { useService } from '@xstate/react';
553
- +import { useActor } from '@xstate/react';
554
-
555
- -const [state, send] = useService(someActor);
556
- +const [state, send] = useActor(someActor);
557
- ```
558
-
559
- ## Resources
560
-
561
- [State Machines in React](https://gedd.ski/post/state-machines-in-react/)
@@ -1,4 +1,4 @@
1
- !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("react"),require("xstate"),require("xstate/src/behaviors")):"function"==typeof define&&define.amd?define(["exports","react","xstate","xstate/src/behaviors"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).XStateReact={},t.React,t.XState,t.behaviors)}(this,(function(t,e,n,r){"use strict";
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
- ***************************************************************************** */var u,i=function(){return(i=Object.assign||function(t){for(var e,n=1,r=arguments.length;n<r;n++)for(var u in e=arguments[n])Object.prototype.hasOwnProperty.call(e,u)&&(t[u]=e[u]);return t}).apply(this,arguments)};function o(t,e){var n="function"==typeof Symbol&&t[Symbol.iterator];if(!n)return t;var r,u,i=n.call(t),o=[];try{for(;(void 0===e||e-- >0)&&!(r=i.next()).done;)o.push(r.value)}catch(t){u={error:t}}finally{try{r&&!r.done&&(n=i.return)&&n.call(i)}finally{if(u)throw u.error}}return o}function c(t,e){for(var n=0,r=e.length,u=t.length;n<r;n++,u++)t[u]=e[n];return t}!function(t){t[t.Effect=1]="Effect",t[t.LayoutEffect=2]="LayoutEffect"}(u||(u={}));var f=e.useLayoutEffect;function a(t){var n=e.useRef();return n.current||(n.current={v:t()}),n.current.v}function s(t,e){var n,r,u=o([[],[]],2),i=u[0],c=u[1];try{for(var f=function(t){var e="function"==typeof Symbol&&Symbol.iterator,n=e&&t[e],r=0;if(n)return n.call(t);if(t&&"number"==typeof t.length)return{next:function(){return t&&r>=t.length&&(t=void 0),{value:t&&t[r++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")}(t),a=f.next();!a.done;a=f.next()){var s=a.value;e(s)?i.push(s):c.push(s)}}catch(t){n={error:t}}finally{try{a&&!a.done&&(r=f.return)&&r.call(f)}finally{if(n)throw n.error}}return[i,c]}function v(t,e){(0,t.exec)(e.context,e._event.data,{action:t,state:e,_event:e._event})()}function l(t,r,l){void 0===r&&(r={});var p=a((function(){return"function"==typeof t?t():t})),d=r.context,y=r.guards,h=r.actions,b=r.activities,g=r.services,x=r.delays,m=r.state,S=function(t,e){var n={};for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&e.indexOf(r)<0&&(n[r]=t[r]);if(null!=t&&"function"==typeof Object.getOwnPropertySymbols){var u=0;for(r=Object.getOwnPropertySymbols(t);u<r.length;u++)e.indexOf(r[u])<0&&Object.prototype.propertyIsEnumerable.call(t,r[u])&&(n[r[u]]=t[r[u]])}return n}(r,["context","guards","actions","activities","services","delays","state"]),O=a((function(){var t={context:d,guards:y,actions:h,activities:b,services:g,delays:x},e=p.withConfig(t,i(i({},p.context),d));return n.interpret(e,i({deferEvents:!0},S))}));return f((function(){var t;return l&&(t=O.subscribe(function(t,e,n){if("object"==typeof t)return t;var r=function(){};return{next:t,error:e||r,complete:n||r}}(l))),function(){null==t||t.unsubscribe()}}),[l]),f((function(){return O.start(m?n.State.create(m):void 0),function(){O.stop()}}),[]),f((function(){Object.assign(O.machine.options.actions,h)}),[h]),f((function(){Object.assign(O.machine.options.services,g)}),[g]),function(t){var n=e.useRef([]),r=e.useRef([]);f((function(){var e=t.subscribe((function(t){var e,i;if(t.actions.length){var f=o(s(t.actions.filter((function(t){return"function"==typeof t.exec&&"__effect"in t.exec})),(function(t){return t.exec.__effect===u.Effect})),2),a=f[0],v=f[1];(e=n.current).push.apply(e,c([],o(a.map((function(e){return[e,t]}))))),(i=r.current).push.apply(i,c([],o(v.map((function(e){return[e,t]})))))}}));return function(){e.unsubscribe()}}),[]),f((function(){for(;r.current.length;){var t=o(r.current.shift(),2);v(t[0],t[1])}})),e.useEffect((function(){for(;n.current.length;){var t=o(n.current.shift(),2);v(t[0],t[1])}}))}(O),O}function p(t,e){var n=function(){for(var e=[],n=0;n<arguments.length;n++)e[n]=arguments[n];return function(){return t.apply(void 0,c([],o(e)))}};return Object.defineProperties(n,{name:{value:"effect:"+t.name},__effect:{value:e}}),n}function d(t){return"state"in t}var y=function(){};function h(t){return"getSnapshot"in t?t.getSnapshot():d(t)?t.state:void 0}function b(t,n){void 0===n&&(n=h);var r=e.useRef(t),u=e.useRef([]),i=o(e.useState((function(){return n(t)})),2),c=i[0],s=i[1],v=a((function(){return function(t){var e=r.current;(function(t){return"deferred"in t})(e)&&e.deferred?u.current.push(t):e.send(t)}}));return f((function(){r.current=t,s(n(t));for(var e=t.subscribe({next:function(t){return s(t)},error:y,complete:y});u.current.length>0;){var i=u.current.shift();t.send(i)}return function(){e.unsubscribe()}}),[t]),[c,v]}var g=function(t,e){return t===e},x=function(t){return"state"in(n=t)&&"machine"in n?0!==("status"in(e=t)?e.status:e._status)?e.state:e.machine.initialState:d(t)?t.state:void 0;var e,n};t.asEffect=function(t){return p(t,u.Effect)},t.asLayoutEffect=function(t){return p(t,u.LayoutEffect)},t.useActor=b,t.useInterpret=l,t.useMachine=function(t,r){void 0===r&&(r={});var u=e.useCallback((function(t){var e=void 0===t.changed&&Object.keys(t.children).length;(t.changed||e)&&a(t)}),[]),i=l(t,r,u),c=o(e.useState((function(){var t=i.machine.initialState;return r.state?n.State.create(r.state):t})),2),f=c[0],a=c[1];return[f,i.send,i]},t.useSelector=function(t,n,r,u){void 0===r&&(r=g),void 0===u&&(u=x);var i=o(e.useState((function(){return n(u(t))})),2),c=i[0],f=i[1],a=e.useRef(c);return e.useEffect((function(){var e=function(t){r(a.current,t)||(f(t),a.current=t)},i=n(u(t));e(i);var o=t.subscribe((function(t){var r=n(t);e(r)}));return function(){return o.unsubscribe()}}),[n,r]),c},t.useService=function(t){return[o(b(t),1)[0],t.send]},t.useSpawn=function(t){return a((function(){return r.spawnBehavior(t)}))},Object.defineProperty(t,"__esModule",{value:!0})}));
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/useActor.js CHANGED
@@ -38,7 +38,15 @@ export function useActor(actorRef, getSnapshot) {
38
38
  var actorRefRef = useRef(actorRef);
39
39
  var deferredEventsRef = useRef([]);
40
40
  var _a = __read(useState(function () { return getSnapshot(actorRef); }), 2), current = _a[0], setCurrent = _a[1];
41
- var send = useConstant(function () { return function (event) {
41
+ var send = useConstant(function () { return function () {
42
+ var args = [];
43
+ for (var _i = 0; _i < arguments.length; _i++) {
44
+ args[_i] = arguments[_i];
45
+ }
46
+ var event = args[0];
47
+ if (process.env.NODE_ENV !== 'production' && args.length > 1) {
48
+ console.warn("Unexpected payload: " + JSON.stringify(args[1]) + ". Only a single event object can be sent to actor send() functions.");
49
+ }
42
50
  var currentActorRef = actorRefRef.current;
43
51
  // If the previous actor is a deferred actor,
44
52
  // queue the events so that they can be replayed
@@ -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 (machine !== initialMachine) {
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
  }
@@ -77,7 +77,7 @@ export function useInterpret(getMachine, options, observerOrListener) {
77
77
  services: services,
78
78
  delays: delays
79
79
  };
80
- var machineWithConfig = machine.withConfig(machineConfig, __assign(__assign({}, machine.context), context));
80
+ var machineWithConfig = machine.withConfig(machineConfig, function () { return (__assign(__assign({}, machine.context), context)); });
81
81
  return interpret(machineWithConfig, __assign({ deferEvents: true }, interpreterOptions));
82
82
  });
83
83
  useIsomorphicLayoutEffect(function () {
@@ -95,15 +95,16 @@ export function useInterpret(getMachine, options, observerOrListener) {
95
95
  service.stop();
96
96
  };
97
97
  }, []);
98
- // Make sure actions and services are kept updated when they change.
98
+ // Make sure options are kept updated when they change.
99
99
  // This mutation assignment is safe because the service instance is only used
100
100
  // in one place -- this hook's caller.
101
101
  useIsomorphicLayoutEffect(function () {
102
102
  Object.assign(service.machine.options.actions, actions);
103
- }, [actions]);
104
- useIsomorphicLayoutEffect(function () {
103
+ Object.assign(service.machine.options.guards, guards);
104
+ Object.assign(service.machine.options.activities, activities);
105
105
  Object.assign(service.machine.options.services, services);
106
- }, [services]);
106
+ Object.assign(service.machine.options.delays, delays);
107
+ }, [actions, guards, activities, services, delays]);
107
108
  useReactEffectActions(service);
108
109
  return service;
109
110
  }
package/es/useSelector.js CHANGED
@@ -1,20 +1,6 @@
1
- var __read = (this && this.__read) || function (o, n) {
2
- var m = typeof Symbol === "function" && o[Symbol.iterator];
3
- if (!m) return o;
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 _a = __read(useState(function () { return selector(getSnapshot(actor)); }), 2), selected = _a[0], setSelected = _a[1];
35
- var selectedRef = useRef(selected);
36
- useEffect(function () {
37
- var updateSelectedIfChanged = function (nextSelected) {
38
- if (!compare(selectedRef.current, nextSelected)) {
39
- setSelected(nextSelected);
40
- selectedRef.current = nextSelected;
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
- var initialSelected = selector(getSnapshot(actor));
44
- updateSelectedIfChanged(initialSelected);
45
- var sub = actor.subscribe(function (emitted) {
46
- var nextSelected = selector(emitted);
47
- updateSelectedIfChanged(nextSelected);
48
- });
49
- return function () { return sub.unsubscribe(); };
50
- }, [selector, compare]);
51
- return selected;
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/es/useSpawn.js CHANGED
@@ -1,4 +1,4 @@
1
- import { spawnBehavior } from 'xstate/src/behaviors';
1
+ import { spawnBehavior } from 'xstate/lib/behaviors';
2
2
  import useConstant from './useConstant';
3
3
  /**
4
4
  * React hook that spawns an `ActorRef` with the specified `behavior`.
package/lib/useActor.js CHANGED
@@ -42,7 +42,15 @@ function useActor(actorRef, getSnapshot) {
42
42
  var actorRefRef = react_1.useRef(actorRef);
43
43
  var deferredEventsRef = react_1.useRef([]);
44
44
  var _a = __read(react_1.useState(function () { return getSnapshot(actorRef); }), 2), current = _a[0], setCurrent = _a[1];
45
- var send = useConstant_1.default(function () { return function (event) {
45
+ var send = useConstant_1.default(function () { return function () {
46
+ var args = [];
47
+ for (var _i = 0; _i < arguments.length; _i++) {
48
+ args[_i] = arguments[_i];
49
+ }
50
+ var event = args[0];
51
+ if (process.env.NODE_ENV !== 'production' && args.length > 1) {
52
+ console.warn("Unexpected payload: " + JSON.stringify(args[1]) + ". Only a single event object can be sent to actor send() functions.");
53
+ }
46
54
  var currentActorRef = actorRefRef.current;
47
55
  // If the previous actor is a deferred actor,
48
56
  // queue the events so that they can be replayed
@@ -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 (machine !== initialMachine) {
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
  }
@@ -80,7 +80,7 @@ function useInterpret(getMachine, options, observerOrListener) {
80
80
  services: services,
81
81
  delays: delays
82
82
  };
83
- var machineWithConfig = machine.withConfig(machineConfig, __assign(__assign({}, machine.context), context));
83
+ var machineWithConfig = machine.withConfig(machineConfig, function () { return (__assign(__assign({}, machine.context), context)); });
84
84
  return xstate_1.interpret(machineWithConfig, __assign({ deferEvents: true }, interpreterOptions));
85
85
  });
86
86
  use_isomorphic_layout_effect_1.default(function () {
@@ -98,15 +98,16 @@ function useInterpret(getMachine, options, observerOrListener) {
98
98
  service.stop();
99
99
  };
100
100
  }, []);
101
- // Make sure actions and services are kept updated when they change.
101
+ // Make sure options are kept updated when they change.
102
102
  // This mutation assignment is safe because the service instance is only used
103
103
  // in one place -- this hook's caller.
104
104
  use_isomorphic_layout_effect_1.default(function () {
105
105
  Object.assign(service.machine.options.actions, actions);
106
- }, [actions]);
107
- use_isomorphic_layout_effect_1.default(function () {
106
+ Object.assign(service.machine.options.guards, guards);
107
+ Object.assign(service.machine.options.activities, activities);
108
108
  Object.assign(service.machine.options.services, services);
109
- }, [services]);
109
+ Object.assign(service.machine.options.delays, delays);
110
+ }, [actions, guards, activities, services, delays]);
110
111
  useReactEffectActions_1.useReactEffectActions(service);
111
112
  return service;
112
113
  }
@@ -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 _a = __read(react_1.useState(function () { return selector(getSnapshot(actor)); }), 2), selected = _a[0], setSelected = _a[1];
38
- var selectedRef = react_1.useRef(selected);
39
- react_1.useEffect(function () {
40
- var updateSelectedIfChanged = function (nextSelected) {
41
- if (!compare(selectedRef.current, nextSelected)) {
42
- setSelected(nextSelected);
43
- selectedRef.current = nextSelected;
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
- var initialSelected = selector(getSnapshot(actor));
47
- updateSelectedIfChanged(initialSelected);
48
- var sub = actor.subscribe(function (emitted) {
49
- var nextSelected = selector(emitted);
50
- updateSelectedIfChanged(nextSelected);
51
- });
52
- return function () { return sub.unsubscribe(); };
53
- }, [selector, compare]);
54
- return selected;
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/lib/useSpawn.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.useSpawn = void 0;
4
- var behaviors_1 = require("xstate/src/behaviors");
4
+ var behaviors_1 = require("xstate/lib/behaviors");
5
5
  var useConstant_1 = require("./useConstant");
6
6
  /**
7
7
  * React hook that spawns an `ActorRef` with the specified `behavior`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xstate/react",
3
- "version": "1.5.0",
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 && npm run test"
42
+ "prepublish": "npm run build"
43
43
  },
44
44
  "bugs": {
45
45
  "url": "https://github.com/davidkpiano/xstate/issues"
@@ -78,9 +78,9 @@
78
78
  "rollup": "^2.35.1",
79
79
  "rollup-plugin-replace": "^2.2.0",
80
80
  "rollup-plugin-terser": "^5.1.2",
81
- "rollup-plugin-typescript2": "^0.29.0",
81
+ "rollup-plugin-typescript2": "^0.30.0",
82
82
  "ts-jest": "^26.5.6",
83
- "typescript": "^4.3.2",
83
+ "typescript": "^4.3.5",
84
84
  "xstate": "*"
85
85
  }
86
86
  }