1

I am currently working on an application using xstate, I have a parent machine that spawns into two different children's machines, the children machines make a fetch to different API endpoint and they all send back to the parent a resolve or reject event depending on the status of the API call , I need help with how to make sure that all fetches are done before transitioning to the idle state on the parent machine.

fetchMachine:

const fetchMachine: FetchMachine =(
  fetchFunction
) => (
{
  id: 'fetch',
  initial: States.Initialize,
  context: {
    response: null,
    error: null
  },
  states: {
    [States.Initialize]: {
      on: {
        'FETCH.REQUEST': {
          target: States.Pending,
        }
      }
    },
    [States.Pending]: {
      invoke: {
        src: 'fetch',
        onDone: {
          target: States.Success,
          actions: ['updateResponse']
        },
        onError: {
          target: States.Failure,
          actions: ['updateError']
        }
      },
    },
    [States.Success]: {
      entry: ['fetchSuccess'],
      on: {
        'FETCH.REQUEST': States.Pending
      }
    },
    [States.Failure]: {
      entry: ['fetchFailure'],
      on: {
        'FETCH.REQUEST': States.Pending
      }
    }
  }
}

The machine above sends the request of the event back to the parent.

The issue now is that the parent machines utilize this machine parallelly, I need help with how to make sure that all the fetches are done before transitioning to the idle state on the parent machine.

dealwap
  • 621
  • 2
  • 14
  • 33

1 Answers1

0

Ideally you'd make use of the final state for a case like this, it's located here in the documentation.

I've re-created your machine in the visualizer, with parallel states that each have a final state to show how it would transition.

Here is the code of the final machine for the sake of completeness:

const parentMachine = Machine({
id: 'your_id_here',
initial: 'pending',
states: {
  pending: {
    on: { CHANGE_EVENT: 'process' }
  },
  process: {
    type: 'parallel',
    states: {
      fetchMachine1: {
        initial: 'initialize',
        states: {
            initialize: {
                on: {
                  'FETCH.REQUEST': {
                    target: 'pending',
                  }
                }
              },
              pending: {
                invoke: {
                  src: 'fetch',
                  onDone: {
                    target: 'success',
                    actions: ['updateResponse']
                  },
                  onError: {
                    target: 'failure',
                    actions: ['updateError']
                  }
                },
              },
              success: {
                entry: ['fetchSuccess'],
                on: {
                  'FETCH.REQUEST': 'pending'
                },
                type: 'final' // 'success' is a final state node for 'fetchMachine1'
              },
              failure: {
                entry: ['fetchFailure'],
                on: {
                  'FETCH.REQUEST': 'pending'
                }
              }
        }
      },
      fetchMachine2: {
        initial: 'initialize',
        states: {
            initialize: {
                on: {
                  'FETCH.REQUEST': {
                    target: 'pending',
                  }
                }
              },
              pending: {
                invoke: {
                  src: 'fetch',
                  onDone: {
                    target: 'success',
                    actions: ['updateResponse']
                  },
                  onError: {
                    target: 'failure',
                    actions: ['updateError']
                  }
                },
              },
              success: {
                entry: ['fetchSuccess'],
                on: {
                  'FETCH.REQUEST': 'pending'
                },
                type: 'final' // 'success' is a final state node for 'fetchMachine1'
              },
              failure: {
                entry: ['fetchFailure'],
                on: {
                  'FETCH.REQUEST': 'pending'
                }
              }
        }
      }
    },
    onDone: 'pending'
  }
}

});

TameBadger
  • 1,580
  • 11
  • 15
  • 1
    So, @TameBadger, when you run this in the visualizer, it seems that both machines listen to the same events. For example, clicking `FETCH.REQUEST` on `fetchMachine1` starts `fetchMachine2`. Is that how parallel works? Do these need to be spawned with an actor to be independent? – Mike Crowe Nov 12 '19 at 12:34
  • @MikeCrowe You can use different events names as well, as long as both reach the final state, the same logic applies. – TameBadger Nov 15 '19 at 09:11
  • This doesn't seem to answer OP's question (or my similar question) since the two fetch machines can be spawned at separate times and finish at separate times. – jfunk Jun 24 '21 at 15:11