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
1
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.0.0-rc.4 to 1.0.0-rc.5

lib/types.d.ts

34

CHANGELOG.md

@@ -5,2 +5,36 @@ # Changelog

## [1.0.0-rc.5]
- You can now schedule actions in `useEffect` or `useLayoutEffect` via:
- `asEffect` - queues the action to be executed in `useEffect`
- `asLayoutEffect` - queues the action to be executed in `useLayoutEffect`
```jsx
import { createMachine } from 'xstate';
import { useMachine, asEffect } from '@xstate/react';
const machine = createMachine({
initial: 'focused',
states: {
focused: {
entry: 'focus'
}
}
});
const Input = () => {
const inputRef = useRef(null);
const [state, send] = useMachine(machine, {
actions: {
focus: asEffect(() => {
inputRef.current && inputRef.current.focus();
})
}
});
return <input ref={inputRef} />;
};
```
## [0.8.1]

@@ -7,0 +41,0 @@

3

lib/index.d.ts

@@ -0,4 +1,3 @@

export { useMachine } from './useMachine';
export { useService } from './useService';
export { useMachine } from './useMachine';
export { useActor } from './useActor';
//# sourceMappingURL=index.d.ts.map
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var useMachine_1 = require("./useMachine");
exports.useMachine = useMachine_1.useMachine;
var useService_1 = require("./useService");
exports.useService = useService_1.useService;
var useMachine_1 = require("./useMachine");
exports.useMachine = useMachine_1.useMachine;
var useActor_1 = require("./useActor");
exports.useActor = useActor_1.useActor;

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

import { ActorRef, Sender } from './ActorRef';
import { EventObject } from 'xstate';
export declare function useActor<TCurrent, TEvent extends EventObject>(actorRef: ActorRef<TCurrent, TEvent>): [TCurrent, Sender<TEvent>];
import { Sender, ActorRefLike } from './types';
import { EventObject, Actor } from 'xstate';
export declare function useActor<TEvent extends EventObject, TEmitted = any>(actorLike: ActorRefLike<TEvent, TEmitted> | Actor): [TEmitted, Sender<TEvent>];
//# sourceMappingURL=useActor.d.ts.map
"use strict";
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __read = (this && this.__read) || function (o, n) {

@@ -20,14 +31,43 @@ var m = typeof Symbol === "function" && o[Symbol.iterator];

var react_1 = require("react");
function useActor(actorRef) {
var _a = __read(react_1.useState(actorRef.current), 2), current = _a[0], setCurrent = _a[1];
var send = actorRef.send;
var xstate_1 = require("xstate");
var useService_1 = require("./useService");
function resolveActor(actorLike) {
if (actorLike instanceof xstate_1.Interpreter) {
return useService_1.fromService(actorLike);
}
if (!('current' in actorLike)) {
return __assign(__assign({ stop: function () {
/* do nothing */
} }, actorLike), { subscribe: actorLike.subscribe, name: actorLike.id, current: actorLike.state });
}
return actorLike;
}
function useActor(actorLike) {
var actor = react_1.useMemo(function () { return resolveActor(actorLike); }, [actorLike]);
var deferredEventsRef = react_1.useRef([]);
var _a = __read(react_1.useState(actor.current), 2), current = _a[0], setCurrent = _a[1];
var send = react_1.useCallback(function (event) {
// If the previous actor is a deferred actor,
// queue the events so that they can be replayed
// on the non-deferred actor.
if (actor.deferred) {
deferredEventsRef.current.push(event);
}
else {
actor.send(event);
}
}, [actor]);
react_1.useEffect(function () {
var sub = actorRef.subscribe(function (latest) {
// this will be called with the current value immediately
setCurrent(latest);
});
return function () { return sub.unsubscribe(); };
}, [actorRef]);
var subscription = actor.subscribe(setCurrent);
// Dequeue deferred events from the previous deferred actor
while (deferredEventsRef.current.length > 0) {
var deferredEvent = deferredEventsRef.current.shift();
actor.send(deferredEvent);
}
return function () {
subscription.unsubscribe();
};
}, [actor]);
return [current, send];
}
exports.useActor = useActor;

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

import { EventObject, StateMachine, State, Interpreter, InterpreterOptions, MachineOptions, Typestate, StateConfig } from 'xstate';
import { EventObject, StateMachine, State, Interpreter, InterpreterOptions, MachineOptions, StateConfig, Typestate, ActionObject, ActionFunction, ActionMeta } from 'xstate';
declare enum ReactEffectType {
Effect = 1,
LayoutEffect = 2
}
export interface ReactActionFunction<TContext, TEvent extends EventObject> {
(context: TContext, event: TEvent, meta: ActionMeta<TContext, TEvent>): () => void;
__effect: ReactEffectType;
}
export interface ReactActionObject<TContext, TEvent extends EventObject> extends ActionObject<TContext, TEvent> {
exec: ReactActionFunction<TContext, TEvent>;
}
export declare function asEffect<TContext, TEvent extends EventObject>(exec: ActionFunction<TContext, TEvent>): ReactActionFunction<TContext, TEvent>;
export declare function asLayoutEffect<TContext, TEvent extends EventObject>(exec: ActionFunction<TContext, TEvent>): ReactActionFunction<TContext, TEvent>;
export declare type ActionStateTuple<TContext, TEvent extends EventObject> = [ReactActionObject<TContext, TEvent>, State<TContext, TEvent>];
interface UseMachineOptions<TContext, TEvent extends EventObject> {

@@ -3,0 +17,0 @@ /**

@@ -40,6 +40,51 @@ "use strict";

};
var __spread = (this && this.__spread) || function () {
for (var ar = [], i = 0; i < arguments.length; i++) ar = ar.concat(__read(arguments[i]));
return ar;
};
Object.defineProperty(exports, "__esModule", { value: true });
var react_1 = require("react");
var use_isomorphic_layout_effect_1 = require("use-isomorphic-layout-effect");
var xstate_1 = require("xstate");
var useConstant_1 = require("./useConstant");
var utils_1 = require("./utils");
var ReactEffectType;
(function (ReactEffectType) {
ReactEffectType[ReactEffectType["Effect"] = 1] = "Effect";
ReactEffectType[ReactEffectType["LayoutEffect"] = 2] = "LayoutEffect";
})(ReactEffectType || (ReactEffectType = {}));
function createReactActionFunction(exec, tag) {
var effectExec = function () {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
// don't execute; just return
return function () {
return exec.apply(void 0, __spread(args));
};
};
Object.defineProperties(effectExec, {
name: { value: "effect:" + exec.name },
__effect: { value: tag }
});
return effectExec;
}
function asEffect(exec) {
return createReactActionFunction(exec, ReactEffectType.Effect);
}
exports.asEffect = asEffect;
function asLayoutEffect(exec) {
return createReactActionFunction(exec, ReactEffectType.LayoutEffect);
}
exports.asLayoutEffect = asLayoutEffect;
function executeEffect(action, state) {
var exec = action.exec;
var originalExec = exec(state.context, state._event.data, {
action: action,
state: state,
_event: state._event
});
originalExec();
}
function useMachine(machine, options) {

@@ -55,4 +100,3 @@ if (options === void 0) { options = {}; }

var context = options.context, guards = options.guards, actions = options.actions, activities = options.activities, services = options.services, delays = options.delays, rehydratedState = options.state, interpreterOptions = __rest(options, ["context", "guards", "actions", "activities", "services", "delays", "state"]);
// Keep a single reference to the invoked machine (the service)
var service = useConstant_1.default(function () {
var _b = __read(useConstant_1.default(function () {
var machineConfig = {

@@ -66,13 +110,47 @@ context: context,

};
var createdMachine = machine.withConfig(machineConfig, __assign(__assign({}, machine.context), context));
// Ensure that actions are not executed (until useEffect() below)
interpreterOptions.execute = false;
return xstate_1.interpret(createdMachine, interpreterOptions).start(rehydratedState ? xstate_1.State.create(rehydratedState) : undefined);
});
// Initialize the state with the initial state.
var _b = __read(react_1.useState(service.state), 2), state = _b[0], setState = _b[1];
// Capture all actions (side-effects) to be executed.
// These will be flushed when they are executed, and avoids the issue of batched events
// sent to the interpreter, which might ignore actions.
var actionStatesRef = react_1.useRef([state]);
var resolvedMachine = machine.withConfig(machineConfig, __assign(__assign({}, machine.context), context));
return [
resolvedMachine,
xstate_1.interpret(resolvedMachine, __assign({ deferEvents: true }, interpreterOptions))
];
}), 2), resolvedMachine = _b[0], service = _b[1];
var _c = __read(react_1.useState(function () {
return rehydratedState
? xstate_1.State.create(rehydratedState)
: resolvedMachine.initialState;
}), 2), state = _c[0], setState = _c[1];
var effectActionsRef = react_1.useRef([]);
var layoutEffectActionsRef = react_1.useRef([]);
use_isomorphic_layout_effect_1.default(function () {
service
.onTransition(function (currentState) {
var _a, _b;
// Only change the current state if:
// - the incoming state is the "live" initial state (since it might have new actors)
// - OR the incoming state actually changed.
//
// The "live" initial state will have .changed === undefined.
var initialStateChanged = currentState.changed === undefined &&
Object.keys(currentState.children).length;
if (currentState.changed || initialStateChanged) {
setState(currentState);
}
if (currentState.actions.length) {
var reactEffectActions = currentState.actions.filter(function (action) {
return (typeof action.exec === 'function' &&
'__effect' in
action.exec);
});
var _c = __read(utils_1.partition(reactEffectActions, function (action) {
return action.exec.__effect === ReactEffectType.Effect;
}), 2), effectActions = _c[0], layoutEffectActions = _c[1];
(_a = effectActionsRef.current).push.apply(_a, __spread(effectActions.map(function (effectAction) { return [effectAction, currentState]; })));
(_b = layoutEffectActionsRef.current).push.apply(_b, __spread(layoutEffectActions.map(function (layoutEffectAction) { return [layoutEffectAction, currentState]; })));
}
})
.start(rehydratedState ? xstate_1.State.create(rehydratedState) : undefined);
return function () {
service.stop();
};
}, []);
// Make sure actions and services are kept updated when they change.

@@ -87,25 +165,16 @@ // This mutation assignment is safe because the service instance is only used

}, [services]);
// this is somewhat weird - this should always be flushed within useLayoutEffect
// but we don't want to receive warnings about useLayoutEffect being used on the server
// so we have to use `useIsomorphicLayoutEffect` to silence those warnings
use_isomorphic_layout_effect_1.default(function () {
while (layoutEffectActionsRef.current.length) {
var _a = __read(layoutEffectActionsRef.current.shift(), 2), layoutEffectAction = _a[0], effectState = _a[1];
executeEffect(layoutEffectAction, effectState);
}
}, [state]); // https://github.com/davidkpiano/xstate/pull/1202#discussion_r429677773
react_1.useEffect(function () {
// Whenever a new state is emitted from the service,
// update the state with that state, but only if
// that state has changed.
service.onTransition(function (currentState) {
if (currentState.changed) {
// capture side-effects to be executed
actionStatesRef.current.push(currentState);
// change state
setState(currentState);
}
});
return function () {
service.stop();
};
}, []);
react_1.useEffect(function () {
// Flush all actions to be executed (per state)
actionStatesRef.current.forEach(function (actionState) {
// Execute all actions for the captured state
service.execute(actionState);
});
actionStatesRef.current = [];
while (effectActionsRef.current.length) {
var _a = __read(effectActionsRef.current.shift(), 2), effectAction = _a[0], effectState = _a[1];
executeEffect(effectAction, effectState);
}
}, [state]);

@@ -112,0 +181,0 @@ return [state, service.send, service];

@@ -1,3 +0,5 @@

import { EventObject, State, Interpreter, Typestate, Event } from 'xstate';
export declare function useService<TContext, TEvent extends EventObject, TTypestate extends Typestate<TContext> = any>(service: Interpreter<TContext, any, TEvent, TTypestate>): [State<TContext, TEvent, any, TTypestate>, (event: Event<TEvent>) => void];
import { EventObject, State, Interpreter, Typestate, Sender } from 'xstate';
import { ActorRef } from './types';
export declare function fromService<TContext, TEvent extends EventObject>(service: Interpreter<TContext, any, TEvent, any>): ActorRef<TEvent, State<TContext, TEvent>>;
export declare function useService<TContext, TEvent extends EventObject, TTypestate extends Typestate<TContext> = any>(service: Interpreter<TContext, any, TEvent, TTypestate>): [State<TContext, TEvent, any, TTypestate>, Sender<TEvent>];
//# sourceMappingURL=useService.d.ts.map
"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 });
var react_1 = require("react");
var useActor_1 = require("./useActor");
var ActorRef_1 = require("./ActorRef");
function fromService(service) {
var machine = service.machine;
return {
send: service.send.bind(service),
subscribe: service.subscribe.bind(service),
stop: service.stop,
current: service.initialized ? service.state : machine.initialState,
name: service.sessionId
};
}
exports.fromService = fromService;
function useService(service) {
var actorRef = react_1.useMemo(function () {
return ActorRef_1.fromService(service);
}, [service]);
var _a = __read(useActor_1.useActor(actorRef), 2), state = _a[0], sendActor = _a[1];
var send = react_1.useCallback(function (event) {
var eventObject = typeof event === 'string' ? { type: event } : event;
sendActor(eventObject);
}, [sendActor]);
return [state, send];
var serviceActor = react_1.useMemo(function () { return fromService(service); }, [service]);
return useActor_1.useActor(serviceActor);
}
exports.useService = useService;
{
"name": "@xstate/react",
"version": "1.0.0-rc.4",
"version": "1.0.0-rc.5",
"description": "XState tools for React",

@@ -37,4 +37,3 @@ "keywords": [

"test": "jest",
"prepublish": "npm run build && npm run test",
"prerelease": "npm publish --tag next"
"prepublish": "npm run build && npm run test"
},

@@ -58,3 +57,4 @@ "bugs": {

"dependencies": {
"use-subscription": "^1.4.0"
"use-isomorphic-layout-effect": "^1.0.0",
"use-subscription": "^1.3.0"
},

@@ -61,0 +61,0 @@ "devDependencies": {

@@ -73,4 +73,50 @@ # @xstate/react

### `useMachine(machine)` with `@xstate/fsm` <Badge text="1.1+"/>
### `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.

@@ -202,6 +248,7 @@

actions: {
notifySuccess: ctx => onResolve(ctx.data)
notifySuccess: (ctx) => onResolve(ctx.data)
},
services: {
fetchData: (_, e) => fetch(`some/api/${e.query}`).then(res => res.json())
fetchData: (_, e) =>
fetch(`some/api/${e.query}`).then((res) => res.json())
}

@@ -257,7 +304,7 @@ });

switch (true) {
case current.matches('idle'):
case state.matches('idle'):
return /* ... */;
case current.matches({ loading: 'user' }):
case state.matches({ loading: 'user' }):
return /* ... */;
case current.matches({ loading: 'friends' }):
case state.matches({ loading: 'friends' }):
return /* ... */;

@@ -325,3 +372,3 @@ default:

useEffect(() => {
const subscription = service.subscribe(state => {
const subscription = service.subscribe((state) => {
// simple state logging

@@ -328,0 +375,0 @@ console.log(state);

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