Fetching Image Data

Learn about creating a simple image-fetching state machine in XState, handling different states, using the invoke property, and exposing fetched data to a parent machine.

Fetching data isn’t always the strong suit of state management libraries. After all, it’s not their basic responsibility. In the case of XState, however, fetching comes very naturally, as every Promise can be modeled as a state machine. At a high level, we need to kick off a function that will be in a default loading state. We’ll wait for something to happen with it—either resolve or reject—and go to the appropriate resolved or rejected states.

Image-fetching machine

Here’s how our image-fetching machine is shaping up:

Press + to interact
// src/machines/fetchImageMachine.js
import { createMachine, assign } from "xstate";
// XState machine definition for fetching images
export const fetchImagesMachine = createMachine({
id: "fetchImages",
// Initial state
initial: "loading",
// Context to hold machine-specific data
context: {
retries: 0, // Number of retry attempts
images: [], // Array to store fetched images
},
// States of the machine
states: {
loading: {
on: {
RESOLVE: "success", // Transition to 'success' state on successful image fetch
REJECT: "failure", // Transition to 'failure' state on unsuccessful image fetch
},
},
success: {
type: "final", // Final state indicating successful image fetch
},
failure: {
on: {
RETRY: {
target: "loading", // Retry fetching images by transitioning back to the 'loading' state
actions: assign({
retries: (context, event) => context.retries + 1, // Increment the number of retry attempts
}),
},
},
},
},
});
// Don't change the code above, add comments as requested.

What we can see here is a very simple machine prepared to describe the process of fetching data from an external source. We have three states:

  • The initial state of loading on line 20.

  • The success state on line 26.

  • The failure states on line 29.

We can see two actions in the loading ...