New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@xstate/react

Package Overview
Dependencies
Maintainers
3
Versions
81
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@xstate/react - npm Package Compare versions

Comparing version 1.6.1 to 1.6.2

39

CHANGELOG.md
# Changelog
## 1.6.2
### Patch Changes
- [#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)
* [#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`.
```js
const machine = createMachine({
initial: 'foo',
context: { id: 1 },
states: {
foo: {
on: {
CHECK: {
target: 'bar',
cond: 'hasOverflown'
}
}
},
bar: {}
}
});
const [id, setId] = useState(1);
const [current, send] = useMachine(
machine.withConfig({
guards: {
hasOverflown: () => id > 1 // id is a reference to an outside value
}
})
);
// later when id updates
setId(2);
// 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`.
```
## 1.6.1

@@ -4,0 +43,0 @@

17

dist/xstate-react.umd.min.js

@@ -1,2 +0,2 @@

!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("react"),require("xstate"),require("xstate/lib/behaviors")):"function"==typeof define&&define.amd?define(["exports","react","xstate","xstate/lib/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";
!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)};
/*! *****************************************************************************

@@ -15,2 +15,15 @@ Copyright (c) Microsoft Corporation.

PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */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 a=e.useLayoutEffect;function f(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 a=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),f=a.next();!f.done;f=a.next()){var s=f.value;e(s)?i.push(s):c.push(s)}}catch(t){n={error:t}}finally{try{f&&!f.done&&(r=a.return)&&r.call(a)}finally{if(n)throw n.error}}return[i,c]}function l(t,e){(0,t.exec)(e.context,e._event.data,{action:t,state:e,_event:e._event})()}function v(t,r,v){void 0===r&&(r={});var p=f((function(){return"function"==typeof t?t():t})),d=r.context,h=r.guards,b=r.actions,y=r.activities,g=r.services,m=r.delays,x=r.state,O=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"]),S=f((function(){var t={context:d,guards:h,actions:b,activities:y,services:g,delays:m},e=p.withConfig(t,(function(){return i(i({},p.context),d)}));return n.interpret(e,i({deferEvents:!0},O))}));return a((function(){var t;return v&&(t=S.subscribe(function(t,e,n){if("object"==typeof t)return t;var r=function(){};return{next:t,error:e||r,complete:n||r}}(v))),function(){null==t||t.unsubscribe()}}),[v]),a((function(){return S.start(x?n.State.create(x):void 0),function(){S.stop()}}),[]),a((function(){Object.assign(S.machine.options.actions,b),Object.assign(S.machine.options.guards,h),Object.assign(S.machine.options.activities,y),Object.assign(S.machine.options.services,g),Object.assign(S.machine.options.delays,m)}),[b,h,y,g,m]),function(t){var n=e.useRef([]),r=e.useRef([]);a((function(){var e=t.subscribe((function(t){var e,i;if(t.actions.length){var a=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),f=a[0],l=a[1];(e=n.current).push.apply(e,c([],o(f.map((function(e){return[e,t]}))))),(i=r.current).push.apply(i,c([],o(l.map((function(e){return[e,t]})))))}}));return function(){e.unsubscribe()}}),[]),a((function(){for(;r.current.length;){var t=o(r.current.shift(),2);l(t[0],t[1])}})),e.useEffect((function(){for(;n.current.length;){var t=o(n.current.shift(),2);l(t[0],t[1])}}))}(S),S}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}function h(t){return"deferred"in t}var b=function(){};function y(t){return"getSnapshot"in t?t.getSnapshot():d(t)?t.state:void 0}function g(t,n){void 0===n&&(n=y);var r=e.useRef(t),u=e.useRef([]),i=o(e.useState((function(){return n(t)})),2),c=i[0],s=i[1],l=f((function(){return function(){for(var t=[],e=0;e<arguments.length;e++)t[e]=arguments[e];var n=t[0],i=r.current;h(i)&&i.deferred?u.current.push(n):i.send(n)}}));return a((function(){r.current=t,s(n(t));for(var e=t.subscribe({next:function(t){return s(t)},error:b,complete:b});u.current.length>0;){var i=u.current.shift();t.send(i)}return function(){e.unsubscribe()}}),[t]),[c,l]}var m=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=g,t.useInterpret=v,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)&&f(t)}),[]),i=v(t,r,u),c=o(e.useState((function(){var t=i.machine.initialState;return r.state?n.State.create(r.state):t})),2),a=c[0],f=c[1];return[a,i.send,i]},t.useSelector=function(t,n,r,u){void 0===r&&(r=m),void 0===u&&(u=x);var i=o(e.useState((function(){return n(u(t))})),2),c=i[0],a=i[1],f=e.useRef(c);return e.useEffect((function(){var e=function(t){r(f.current,t)||(a(t),f.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(g(t),1)[0],t.send]},t.useSpawn=function(t){return f((function(){return r.spawnBehavior(t)}))},Object.defineProperty(t,"__esModule",{value:!0})}));
***************************************************************************** */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
/*
object-assign
(c) Sindre Sorhus
@license MIT
*/}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})));
/** @license React vundefined
* use-subscription.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/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})}));

2

es/useInterpret.js

@@ -65,3 +65,3 @@ var __assign = (this && this.__assign) || function () {

var _a = __read(useState(machine), 1), initialMachine = _a[0];
if (machine !== initialMachine) {
if (getMachine !== initialMachine) {
console.warn('Machine given to `useMachine` has changed between renders. This is not supported and might lead to unexpected results.\n' +

@@ -68,0 +68,0 @@ 'Please make sure that you pass the same Machine as argument each time.');

@@ -1,18 +0,4 @@

var __read = (this && this.__read) || function (o, n) {
var m = typeof Symbol === "function" && o[Symbol.iterator];
if (!m) return o;
var i = m.call(o), r, ar = [], e;
try {
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
}
catch (error) { e = { error: error }; }
finally {
try {
if (r && !r.done && (m = i["return"])) m.call(i);
}
finally { if (e) throw e.error; }
}
return ar;
};
import { useEffect, useRef, useState } from 'react';
import { useMemo, useRef } from 'react';
import { useSubscription } from 'use-subscription';
import useIsomorphicLayoutEffect from 'use-isomorphic-layout-effect';
import { isActorWithState } from './useActor';

@@ -34,20 +20,47 @@ import { getServiceSnapshot } from './useService';

if (getSnapshot === void 0) { getSnapshot = defaultGetSnapshot; }
var _a = __read(useState(function () { return selector(getSnapshot(actor)); }), 2), selected = _a[0], setSelected = _a[1];
var selectedRef = useRef(selected);
useEffect(function () {
var updateSelectedIfChanged = function (nextSelected) {
if (!compare(selectedRef.current, nextSelected)) {
setSelected(nextSelected);
selectedRef.current = nextSelected;
var latestSelectorRef = useRef(selector);
var subscription = useMemo(function () {
var snapshot = getSnapshot(actor);
var current = selector(snapshot);
var notifySubscriber;
return {
getSnapshot: function () { return snapshot; },
getCurrentValue: function () { return current; },
setCurrentValue: function (newCurrent) {
current = newCurrent;
notifySubscriber === null || notifySubscriber === void 0 ? void 0 : notifySubscriber();
},
subscribe: function (callback) {
notifySubscriber = callback;
var sub = actor.subscribe(function (emitted) {
snapshot = emitted;
var next = latestSelectorRef.current(emitted);
if (!compare(current, next)) {
current = next;
callback();
}
});
return function () {
sub.unsubscribe();
};
}
};
var initialSelected = selector(getSnapshot(actor));
updateSelectedIfChanged(initialSelected);
var sub = actor.subscribe(function (emitted) {
var nextSelected = selector(emitted);
updateSelectedIfChanged(nextSelected);
});
return function () { return sub.unsubscribe(); };
}, [selector, compare]);
return selected;
// intentionally omit `getSnapshot` and `compare`
// - `getSnapshot`: it is only supposed to read the "initial" snapshot of an actor
// - `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)
}, [actor]);
var currentSelected = useSubscription(subscription);
if (latestSelectorRef.current !== selector) {
var selected = selector(subscription.getSnapshot());
if (!compare(currentSelected, selected)) {
currentSelected = selected;
}
}
useIsomorphicLayoutEffect(function () {
latestSelectorRef.current = selector;
// required so we don't cause a rerender by setting state (this could create infinite rerendering loop with inline selectors)
// 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
subscription.setCurrentValue(currentSelected);
});
return currentSelected;
}

@@ -68,3 +68,3 @@ "use strict";

var _a = __read(react_1.useState(machine), 1), initialMachine = _a[0];
if (machine !== initialMachine) {
if (getMachine !== initialMachine) {
console.warn('Machine given to `useMachine` has changed between renders. This is not supported and might lead to unexpected results.\n' +

@@ -71,0 +71,0 @@ 'Please make sure that you pass the same Machine as argument each time.');

"use strict";
var __read = (this && this.__read) || function (o, n) {
var m = typeof Symbol === "function" && o[Symbol.iterator];
if (!m) return o;
var i = m.call(o), r, ar = [], e;
try {
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
}
catch (error) { e = { error: error }; }
finally {
try {
if (r && !r.done && (m = i["return"])) m.call(i);
}
finally { if (e) throw e.error; }
}
return ar;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.useSelector = void 0;
var react_1 = require("react");
var use_subscription_1 = require("use-subscription");
var use_isomorphic_layout_effect_1 = require("use-isomorphic-layout-effect");
var useActor_1 = require("./useActor");

@@ -37,21 +23,48 @@ var useService_1 = require("./useService");

if (getSnapshot === void 0) { getSnapshot = defaultGetSnapshot; }
var _a = __read(react_1.useState(function () { return selector(getSnapshot(actor)); }), 2), selected = _a[0], setSelected = _a[1];
var selectedRef = react_1.useRef(selected);
react_1.useEffect(function () {
var updateSelectedIfChanged = function (nextSelected) {
if (!compare(selectedRef.current, nextSelected)) {
setSelected(nextSelected);
selectedRef.current = nextSelected;
var latestSelectorRef = react_1.useRef(selector);
var subscription = react_1.useMemo(function () {
var snapshot = getSnapshot(actor);
var current = selector(snapshot);
var notifySubscriber;
return {
getSnapshot: function () { return snapshot; },
getCurrentValue: function () { return current; },
setCurrentValue: function (newCurrent) {
current = newCurrent;
notifySubscriber === null || notifySubscriber === void 0 ? void 0 : notifySubscriber();
},
subscribe: function (callback) {
notifySubscriber = callback;
var sub = actor.subscribe(function (emitted) {
snapshot = emitted;
var next = latestSelectorRef.current(emitted);
if (!compare(current, next)) {
current = next;
callback();
}
});
return function () {
sub.unsubscribe();
};
}
};
var initialSelected = selector(getSnapshot(actor));
updateSelectedIfChanged(initialSelected);
var sub = actor.subscribe(function (emitted) {
var nextSelected = selector(emitted);
updateSelectedIfChanged(nextSelected);
});
return function () { return sub.unsubscribe(); };
}, [selector, compare]);
return selected;
// intentionally omit `getSnapshot` and `compare`
// - `getSnapshot`: it is only supposed to read the "initial" snapshot of an actor
// - `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)
}, [actor]);
var currentSelected = use_subscription_1.useSubscription(subscription);
if (latestSelectorRef.current !== selector) {
var selected = selector(subscription.getSnapshot());
if (!compare(currentSelected, selected)) {
currentSelected = selected;
}
}
use_isomorphic_layout_effect_1.default(function () {
latestSelectorRef.current = selector;
// required so we don't cause a rerender by setting state (this could create infinite rerendering loop with inline selectors)
// 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
subscription.setCurrentValue(currentSelected);
});
return currentSelected;
}
exports.useSelector = useSelector;
{
"name": "@xstate/react",
"version": "1.6.1",
"version": "1.6.2",
"description": "XState tools for React",

@@ -42,3 +42,3 @@ "keywords": [

"test": "jest",
"prepublish": "npm run build && npm run test"
"prepublish": "npm run build"
},

@@ -45,0 +45,0 @@ "bugs": {

# @xstate/react
[[toc]]
This package contains utilities for using [XState](https://github.com/statelyai/xstate) with [React](https://github.com/facebook/react/).
## Quick Start
- [Read the full documentation in the XState docs](https://xstate.js.org/docs/packages/xstate-react/).
- [Read our contribution guidelines](https://github.com/statelyai/xstate/blob/main/CONTRIBUTING.md).
## Quick start
1. Install `xstate` and `@xstate/react`:

@@ -60,511 +63,1 @@

```
## Examples
- [XState + React TodoMVC (CodeSandbox)](https://codesandbox.io/s/xstate-todomvc-33wr94qv1)
## API
### `useMachine(machine, options?)`
A [React hook](https://reactjs.org/hooks) that interprets the given `machine` and starts a service that runs for the lifetime of the component.
**Arguments**
- `machine` - An [XState machine](https://xstate.js.org/docs/guides/machines.html) or a function that lazily returns a machine:
```js
// existing machine
const [state, send] = useMachine(machine);
// lazily-created machine
const [state, send] = useMachine(() =>
createMachine({
/* ... */
})
);
```
- `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`.
**Returns** a tuple of `[state, send, service]`:
- `state` - Represents the current state of the machine as an XState `State` object.
- `send` - A function that sends events to the running service.
- `service` - The created service.
### `useService(service)`
::: warning Deprecated
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.
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:
```diff
const [state, send] = useActor(service);
-send('CLICK', { x: 0, y: 3 });
+send({ type: 'CLICK', x: 0, y: 3 });
```
:::
A [React hook](https://reactjs.org/hooks) that subscribes to state changes from an existing [service](https://xstate.js.org/docs/guides/interpretation.html).
**Arguments**
- `service` - An [XState service](https://xstate.js.org/docs/guides/interpretation.html).
**Returns** a tuple of `[state, send]`:
- `state` - Represents the current state of the service as an XState `State` object.
- `send` - A function that sends events to the running service.
### `useActor(actor, getSnapshot?)`
A [React hook](https://reactjs.org/hooks) that subscribes to emitted changes from an existing [actor](https://xstate.js.org/docs/guides/actors.html).
**Arguments**
- `actor` - an actor-like object that contains `.send(...)` and `.subscribe(...)` methods.
- `getSnapshot` - a function that should return the latest emitted value from the `actor`.
- Defaults to attempting to get the `actor.state`, or returning `undefined` if that does not exist.
```js
const [state, send] = useActor(someSpawnedActor);
// with custom actors
const [state, send] = useActor(customActor, (actor) => {
// implementation-specific pseudocode example:
return actor.getLastEmittedValue();
});
```
### `useInterpret(machine, options?, observer?)`
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.
_Since 1.3.0_
**Arguments**
- `machine` - An [XState machine](https://xstate.js.org/docs/guides/machines.html) or a function that lazily returns a machine.
- `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`.
- `observer` (optional) - an observer or listener that listens to state updates:
- an observer (e.g., `{ next: (state) => {/* ... */} }`)
- or a listener (e.g., `(state) => {/* ... */}`)
```js
import { useInterpret } from '@xstate/react';
import { someMachine } from '../path/to/someMachine';
const App = () => {
const service = useInterpret(someMachine);
// ...
};
```
With options + listener:
```js
// ...
const App = () => {
const service = useInterpret(
someMachine,
{
actions: {
/* ... */
}
},
(state) => {
// subscribes to state changes
console.log(state);
}
);
// ...
};
```
### `useSelector(actor, selector, compare?, getSnapshot?)`
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.
_Since 1.3.0_
**Arguments**
- `actor` - a service or an actor-like object that contains `.send(...)` and `.subscribe(...)` methods.
- `selector` - a function that takes in an actor's "current state" (snapshot) as an argument and returns the desired selected value.
- `compare` (optional) - a function that determines if the current selected value is the same as the previous selected value.
- `getSnapshot` (optional) - a function that should return the latest emitted value from the `actor`.
- Defaults to attempting to get the `actor.state`, or returning `undefined` if that does not exist. Will automatically pull the state from services.
```js
import { useSelector } from '@xstate/react';
// tip: optimize selectors by defining them externally when possible
const selectCount = (state) => state.context.count;
const App = ({ service }) => {
const count = useSelector(service, selectCount);
// ...
};
```
With `compare` function:
```js
// ...
const selectUser = (state) => state.context.user;
const compareUser = (prevUser, nextUser) => prevUser.id === nextUser.id;
const App = ({ service }) => {
const user = useSelector(service, selectUser, compareUser);
// ...
};
```
With `useInterpret(...)`:
```js
import { useInterpret, useSelector } from '@xstate/react';
import { someMachine } from '../path/to/someMachine';
const selectCount = (state) => state.context.count;
const App = ({ service }) => {
const service = useInterpret(someMachine);
const count = useSelector(service, selectCount);
// ...
};
```
### `asEffect(action)`
Ensures that the `action` is executed as an effect in `useEffect`, rather than being immediately executed.
**Arguments**
- `action` - An action function (e.g., `(context, event) => { alert(context.message) })`)
**Returns** a special action function that wraps the original so that `useMachine` knows to execute it in `useEffect`.
**Example**
```jsx
const machine = createMachine({
initial: 'focused',
states: {
focused: {
entry: 'focus'
}
}
});
const Input = () => {
const inputRef = useRef(null);
const [state, send] = useMachine(machine, {
actions: {
focus: asEffect((context, event) => {
inputRef.current && inputRef.current.focus();
})
}
});
return <input ref={inputRef} />;
};
```
### `asLayoutEffect(action)`
Ensures that the `action` is executed as an effect in `useLayoutEffect`, rather than being immediately executed.
**Arguments**
- `action` - An action function (e.g., `(context, event) => { alert(context.message) })`)
**Returns** a special action function that wraps the original so that `useMachine` knows to execute it in `useLayoutEffect`.
### `useMachine(machine)` with `@xstate/fsm`
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.
This special `useMachine` hook is imported from `@xstate/react/fsm`
**Arguments**
- `machine` - An [XState finite state machine (FSM)](https://xstate.js.org/docs/packages/xstate-fsm/).
- `options` - An optional `options` object.
**Returns** a tuple of `[state, send, service]`:
- `state` - Represents the current state of the machine as an `@xstate/fsm` `StateMachine.State` object.
- `send` - A function that sends events to the running service.
- `service` - The created `@xstate/fsm` service.
**Example**
```js
import { useEffect } from 'react';
import { useMachine } from '@xstate/react/fsm';
import { createMachine } from '@xstate/fsm';
const context = {
data: undefined
};
const fetchMachine = createMachine({
id: 'fetch',
initial: 'idle',
context,
states: {
idle: {
on: { FETCH: 'loading' }
},
loading: {
entry: ['load'],
on: {
RESOLVE: {
target: 'success',
actions: assign({
data: (context, event) => event.data
})
}
}
},
success: {}
}
});
const Fetcher = ({
onFetch = () => new Promise((res) => res('some data'))
}) => {
const [state, send] = useMachine(fetchMachine, {
actions: {
load: () => {
onFetch().then((res) => {
send({ type: 'RESOLVE', data: res });
});
}
}
});
switch (state.value) {
case 'idle':
return <button onClick={(_) => send('FETCH')}>Fetch</button>;
case 'loading':
return <div>Loading...</div>;
case 'success':
return (
<div>
Success! Data: <div data-testid="data">{state.context.data}</div>
</div>
);
default:
return null;
}
};
```
## Configuring Machines
Existing machines can be configured by passing the machine options as the 2nd argument of `useMachine(machine, options)`.
Example: the `'fetchData'` service and `'notifySuccess'` action are both configurable:
```js
const fetchMachine = createMachine({
id: 'fetch',
initial: 'idle',
context: {
data: undefined,
error: undefined
},
states: {
idle: {
on: { FETCH: 'loading' }
},
loading: {
invoke: {
src: 'fetchData',
onDone: {
target: 'success',
actions: assign({
data: (_, event) => event.data
})
},
onError: {
target: 'failure',
actions: assign({
error: (_, event) => event.data
})
}
}
},
success: {
entry: 'notifySuccess',
type: 'final'
},
failure: {
on: {
RETRY: 'loading'
}
}
}
});
const Fetcher = ({ onResolve }) => {
const [state, send] = useMachine(fetchMachine, {
actions: {
notifySuccess: (ctx) => onResolve(ctx.data)
},
services: {
fetchData: (_, e) =>
fetch(`some/api/${e.query}`).then((res) => res.json())
}
});
switch (state.value) {
case 'idle':
return (
<button onClick={() => send('FETCH', { query: 'something' })}>
Search for something
</button>
);
case 'loading':
return <div>Searching...</div>;
case 'success':
return <div>Success! Data: {state.context.data}</div>;
case 'failure':
return (
<>
<p>{state.context.error.message}</p>
<button onClick={() => send('RETRY')}>Retry</button>
</>
);
default:
return null;
}
};
```
## Matching States
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).
We can do this with `if/else if/else` blocks:
```js
// ...
if (state.matches('idle')) {
return /* ... */;
} else if (state.matches({ loading: 'user' })) {
return /* ... */;
} else if (state.matches({ loading: 'friends' })) {
return /* ... */;
} else {
return null;
}
```
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`:
```js
switch (true) {
case state.matches('idle'):
return /* ... */;
case state.matches({ loading: 'user' }):
return /* ... */;
case state.matches({ loading: 'friends' }):
return /* ... */;
default:
return null;
}
```
A ternary statement can also be considered, especially within rendered JSX:
```jsx
const Loader = () => {
const [state, send] = useMachine(/* ... */);
return (
<div>
{state.matches('idle') ? (
<Loader.Idle />
) : state.matches({ loading: 'user' }) ? (
<Loader.LoadingUser />
) : state.matches({ loading: 'friends' }) ? (
<Loader.LoadingFriends />
) : null}
</div>
);
};
```
## Persisted and Rehydrated State
You can persist and rehydrate state with `useMachine(...)` via `options.state`:
```js
// ...
// Get the persisted state config object from somewhere, e.g. localStorage
const persistedState = JSON.parse(localStorage.getItem('some-persisted-state-key')) || someMachine.initialState;
const App = () => {
const [state, send] = useMachine(someMachine, {
state: persistedState // provide persisted state config object here
});
// state will initially be that persisted state, not the machine's initialState
return (/* ... */)
}
```
## Services
The `service` created in `useMachine(machine)` can be referenced as the third returned value:
```js
// vvvvvvv
const [state, send, service] = useMachine(someMachine);
```
You can subscribe to that service's state changes with the [`useEffect` hook](https://reactjs.org/docs/hooks-effect.html):
```js
// ...
useEffect(() => {
const subscription = service.subscribe((state) => {
// simple state logging
console.log(state);
});
return subscription.unsubscribe;
}, [service]); // note: service should never change
```
## Migration from 0.x
- For spawned actors created using `invoke` or `spawn(...)`, use the `useActor()` hook instead of `useService()`:
```diff
-import { useService } from '@xstate/react';
+import { useActor } from '@xstate/react';
-const [state, send] = useService(someActor);
+const [state, send] = useActor(someActor);
```
## Resources
[State Machines in React](https://gedd.ski/post/state-machines-in-react/)
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc