-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Implement predictableActionArguments feature flag
#3289
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
430362d
8434b59
76d525f
8129108
7e1efc4
493d2d5
0ea1b04
1516636
6fef7a5
bb0e0fb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| --- | ||
| 'xstate': minor | ||
| --- | ||
|
|
||
| A new [`predictableActionArguments`](https://xstate.js.org/docs/guides/actions.html) feature flag has been added that allows you to opt into some fixed behaviors that will be the default in v5. With this flag: | ||
|
|
||
| - XState will always call an action with the event directly responsible for the related transition, | ||
| - you also automatically opt-into [`preserveActionOrder`](https://xstate.js.org/docs/guides/context.html#action-order). | ||
|
|
||
| Please be aware that you might not able to use `state` from the `meta` argument when using this flag. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,25 @@ | ||
| # Actions | ||
|
|
||
| ::: warning | ||
|
|
||
| It is advised to configure `predictableActionArguments: true` at the top-level of your machine config, like this: | ||
|
|
||
| ```js | ||
| createMachine({ | ||
| predictableActionArguments: true | ||
| // ... | ||
| }); | ||
| ``` | ||
|
|
||
| This flag is an opt into some fixed behaviors that will be the default in v5. With this flag: | ||
|
|
||
| - XState will always call an action with the event directly responsible for the related transition, | ||
| - you also automatically opt-into [`preserveActionOrder`](https://xstate.js.org/docs/guides/context.html#action-order). | ||
|
|
||
| Please be aware that you might not able to use `state` from the `meta` argument when using this flag. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can y'all add more information how to migrate? We had trouble finding a replacement for using this. Thanks!
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you open a discussion that would describe your use case for using this property? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
|
||
| ::: | ||
|
|
||
| Actions are fire-and-forget [effects](./effects.md). They can be declared in three ways: | ||
|
|
||
| - `entry` actions are executed upon entering a state | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -39,7 +39,8 @@ import { | |
| StopActionObject, | ||
| Cast, | ||
| EventFrom, | ||
| AnyActorRef | ||
| AnyActorRef, | ||
| PredictableActionArgumentsExec | ||
| } from './types'; | ||
| import * as actionTypes from './actionTypes'; | ||
| import { | ||
|
|
@@ -628,6 +629,7 @@ export function resolveActions<TContext, TEvent extends EventObject>( | |
| currentContext: TContext, | ||
| _event: SCXML.Event<TEvent>, | ||
| actions: Array<ActionObject<TContext, TEvent>>, | ||
| predictableExec?: PredictableActionArgumentsExec, | ||
| preserveActionOrder: boolean = false | ||
| ): [Array<ActionObject<TContext, TEvent>>, TContext] { | ||
| const [assignActions, otherActions] = preserveActionOrder | ||
|
|
@@ -650,15 +652,16 @@ export function resolveActions<TContext, TEvent extends EventObject>( | |
| otherActions | ||
| .map((actionObject) => { | ||
| switch (actionObject.type) { | ||
| case actionTypes.raise: | ||
| case actionTypes.raise: { | ||
| return resolveRaise(actionObject as RaiseAction<TEvent>); | ||
| } | ||
| case actionTypes.send: | ||
| const sendAction = resolveSend( | ||
| actionObject as SendAction<TContext, TEvent, AnyEventObject>, | ||
| updatedContext, | ||
| _event, | ||
| machine.options.delays as any | ||
| ) as ActionObject<TContext, TEvent>; // TODO: fix ActionTypes.Init | ||
| ) as SendActionObject<TContext, TEvent>; // TODO: fix ActionTypes.Init | ||
|
|
||
| if (!IS_PRODUCTION) { | ||
| // warn after resolving as we can create better contextual message here | ||
|
|
@@ -670,13 +673,20 @@ export function resolveActions<TContext, TEvent extends EventObject>( | |
| ); | ||
| } | ||
|
|
||
| if (sendAction.to !== SpecialTargets.Internal) { | ||
| predictableExec?.(sendAction, updatedContext, _event); | ||
| } | ||
|
|
||
| return sendAction; | ||
| case actionTypes.log: | ||
| return resolveLog( | ||
| case actionTypes.log: { | ||
| const resolved = resolveLog( | ||
| actionObject as LogAction<TContext, TEvent>, | ||
| updatedContext, | ||
| _event | ||
| ); | ||
| predictableExec?.(resolved, updatedContext, _event); | ||
| return resolved; | ||
| } | ||
| case actionTypes.choose: { | ||
| const chooseAction = actionObject as ChooseAction<TContext, TEvent>; | ||
| const matchedActions = chooseAction.conds.find((condition) => { | ||
|
|
@@ -691,7 +701,7 @@ export function resolveActions<TContext, TEvent extends EventObject>( | |
| guard, | ||
| updatedContext, | ||
| _event, | ||
| currentState as any | ||
| (!predictableExec ? currentState : undefined) as any | ||
| ) | ||
| ); | ||
| })?.actions; | ||
|
|
@@ -712,6 +722,7 @@ export function resolveActions<TContext, TEvent extends EventObject>( | |
| toArray(matchedActions), | ||
| machine.options.actions as any | ||
| ), | ||
| predictableExec, | ||
| preserveActionOrder | ||
| ); | ||
| updatedContext = resolvedContextFromChoose; | ||
|
|
@@ -735,25 +746,28 @@ export function resolveActions<TContext, TEvent extends EventObject>( | |
| toArray(matchedActions), | ||
| machine.options.actions as any | ||
| ), | ||
| predictableExec, | ||
| preserveActionOrder | ||
| ); | ||
| updatedContext = resolvedContext; | ||
| preservedContexts?.push(updatedContext); | ||
| return resolvedActionsFromPure; | ||
| } | ||
| case actionTypes.stop: { | ||
| return resolveStop( | ||
| const resolved = resolveStop( | ||
| actionObject as StopAction<TContext, TEvent>, | ||
| updatedContext, | ||
| _event | ||
| ); | ||
| predictableExec?.(resolved, updatedContext, _event); | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This leads to mutating
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this is OK given the current state of things - I think though that this should be fixed in v5 |
||
| return resolved; | ||
| } | ||
| case actionTypes.assign: { | ||
| updatedContext = updateContext( | ||
| updatedContext, | ||
| _event, | ||
| [actionObject as AssignAction<TContext, TEvent>], | ||
| currentState | ||
| !predictableExec ? currentState : undefined | ||
| ); | ||
| preservedContexts?.push(updatedContext); | ||
| break; | ||
|
|
@@ -764,7 +778,9 @@ export function resolveActions<TContext, TEvent extends EventObject>( | |
| machine.options.actions as any | ||
| ); | ||
| const { exec } = resolvedActionObject; | ||
| if (exec && preservedContexts) { | ||
| if (predictableExec) { | ||
| predictableExec(resolvedActionObject, updatedContext, _event); | ||
| } else if (exec && preservedContexts) { | ||
| const contextIndex = preservedContexts.length - 1; | ||
| resolvedActionObject = { | ||
| ...resolvedActionObject, | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add the XState version this applies to?