statelyai / xstate

Actor-based state management & orchestration for complex app logic.
https://stately.ai/docs
MIT License
27.21k stars 1.26k forks source link

Bug: action objects returned by invoke.onDone.actions functions don't execute #5043

Closed ersinakinci closed 3 months ago

ersinakinci commented 3 months ago

XState version

XState version 5

Description

Not sure whether this is a bug or a feature request 😄

When I invoke a promise actor, I can use a function to generate an action object to execute an action during onDone:

validating: {
  invoke: {
    src: "validateStep",
    input: ({ context }) => ({
      schema: context.steps[context.currentStep].schema,
      data: context.validationDraft,
    }),
    onDone: [
      {
        target: "persisting",
        actions: ({ event }) => {
          console.log(event.output); //  for example, prints { name: "Testing" }

          // This action object never triggers an execution
          return {
            type: "persistDraft",
            params: { data: event.output },
          };
        },
      },
    ],
  },
},

The function definitely runs, but the action object that it returned doesn't seem to trigger any execution.

When I use assign directly, however, I can trigger an action execution:

validating: {
  invoke: {
    src: "validateStep",
    input: ({ context }) => ({
      schema: context.steps[context.currentStep].schema,
      data: context.validationDraft,
    }),
    onDone: [
      {
        target: "persisting",
        // Correctly assigns the event output to context
        actions: assign({
          draft: ({event}) => event.output
        }),
      },
    ],
  },
},

Expected result

I expect the persistDraft action that I've defined in the actions section of my setup config to execute when I return an object with the shape { type: "persistDraft" } from a invoke.onDone.actions function.

Actual result

The onDone function executes and returns an action object, but it doesn't trigger the associated action.

Reproduction

https://stackblitz.com/edit/github-6l6yru?file=src%2FfeedbackMachine.ts

Additional context

No response

ersinakinci commented 3 months ago

Never mind, I figured out what I was doing wrong. The function needs to be assigned to the params property rather than being an element of the actions array. This works:

actions: {
  type: "persistDraft",
  params: ({ event }) => ({ data: event.output }),
},
davidkpiano commented 3 months ago

Hey @ersinakinci - as you probably gathered, this type of dynamic action is not supported:

        actions: ({ event }) => {
          console.log(event.output); //  for example, prints { name: "Testing" }

          // This action object never triggers an execution
          return {
            type: "persistDraft",
            params: { data: event.output },
          };
        },

The reason is because XState needs to assume that executing this function would be impure. You can use enqueueActions(…) instead:

actions: enqueueActions(({ event, enqueue }) => {
  // ...
  enqueue({
    type: 'persistDraft',
    params: { data: event.output }
  })
})

But in your case, the dynamic params solution is better 👍