0

I have an xstate state machine with 3 states. When data is fetched and final state isreached, the state machine is 'Done'. I would like to go back to initial state of idle after 'finally completing the task'. How do I accomplish that? For example,

states: {
idle: {
  id: 'initialState'
},
waitingForA: {
  invoke: { /*Promise*/ },
  onDone: { target: 'wiatingForB' },
  onError: { alert(); }
},
waitingForB: {
      invoke: { /*Promise*/ },
      onDone: {
        target: 'waitingForC',
        actions: assign({ bReturnCode: (context, event) => event.data, })
      }
    },
    waitingForC: {
      invoke: { /*Promise*/ },
      onDone: {
        target: 'success',
        actions: assign({ cReturnCode: (context, event) => event.data, })
      },
      onError{
           target: 'showAlert'
      }
    },
    success: {
      type: 'final' //here, I would like ot go back to idle state;
    },
    final: {target: 'idle'}
  }

}
SoftwareDveloper
  • 559
  • 1
  • 5
  • 18
  • I just wanted to mention that your code wouldn't be working in the first place. The `onDone` and `onError` events need to be inside the `invoke`. Also, the formatting is a bit messy. Maybe you can do that better in future posts. In the link below are some tips on how to create a good minimal example that helps others answer your question faster ;) https://stackoverflow.com/help/minimal-reproducible-example – Tobi Obeck Oct 31 '22 at 10:00

1 Answers1

0

You can use an always transition which transitions to the idle state without the need to explicitly fireing an event. Just keep in mind that the success state is not allowed to be a final state otherwise the execution of the service would stop.

You can read more about these automatic ('eventless') transitions in the XState docs here.

  success: {
    // is not allowed to be a final state
    // because you still want to transition to idle
    // and the execution of the service would stop
    always: 'idle',
  },

Full machine definition

    id: 'some-machine'
    predictableActionArguments: true,
    context: { bReturnCode: null },
    initial: 'idle',
    states: {
      idle: {
        id: 'initialState',
        on: {
          start: 'waitingForB'
        }
      },
      waitingForA: { /* ... */ },
      showAlert: { /* ... */ },
      waitingForB: {
        invoke: {
          "id": "getStuffC",
          "src": (context, event) => debugFetch,
          onDone: {
            target: 'waitingForC',
            actions: assign({ bReturnCode: (context, event) => event.data })
          },
          onError: {
            target: 'showAlert'
          }
        },
      },
      waitingForC: {
        invoke: {
          "id": "getStuffC",
          "src": (context, event) => debugFetch,
          onDone: {
            target: 'success',
            actions: assign({ cReturnCode: (context, event) => event.data })
          },
          onError: {
            target: 'showAlert'
          }
        },
      },
      success: {
        // is not allowed to be a final state
        // because you still want to transition to idle
        // and the execution of the service would stop
        always: 'idle',
      },
    }

function debugFetch() {
  return new Promise((resolve, reject) => {
    const delayInMs = 1000
    setTimeout(() => {
      if (Math.random() <= 0.01) {
        reject('some error')
      }
      resolve('success')
    }, delayInMs)
  })
}

Tobi Obeck
  • 1,918
  • 1
  • 19
  • 31