Search code examples
javascriptreactjsxstate

Xstate Getting warning: No implementation found for action type


I have similar actions ADD_THOUGHT_TIME_STAMP in two different states, so I tried moving that action to the actions of xStat, by fallowing the official documentation.

But I keep getting an error: "No implementation found for action type addThoughtTimStamp"

Here is the code that works:

import { Machine, assign } from "xstate";
import {
  ADD_PRIMING_STAGE_ACTION,
  ADD_WORK_STAGE_ACTION,
  ADD_THOUGHT_TIME_STAMP,
  CREATE,
  GO_TO_RESULTS_ACTION,
  PRIMING,
  WORK,
  RESULTS,
  CREATE_NEW_TASK_ACTION,
} from "../types";

export const taskMachine = Machine({
  id: "taskMachineID",
  initial: CREATE,
  context: {
    taskName: "",
    taskStages: [],
  },
  states: {
    CREATE: {
      on: {
        [CREATE_NEW_TASK_ACTION]: {
          // CREATE_NEW TASK is an action.
          target: PRIMING,
          actions: assign((context, data) => {
            console.log("testing action works");
            return {
              ...context,
              taskName: data.taskName,
              taskStages: [{ stageName: "priming", timeOfThoughts: [] }],
            };
          }),
        },
      },
    },
    [PRIMING]: {
      on: {
        [ADD_WORK_STAGE_ACTION]: {
          target: WORK,
          actions: assign((context) => {
            return {
              ...context,
              taskStages: [
                ...context.taskStages,
                { stageName: "work", timeOfThoughts: [] },
              ],
            };
          }),
        },
        [ADD_THOUGHT_TIME_STAMP]: {
          actions: assign((context, { thoughtTime }) => {
            const lastStage =
              context.taskStages[context.taskStages.length - 1].timeOfThoughts;
            console.log(lastStage);
            lastStage.push(thoughtTime);
            return {
              ...context,
            };
          }),
        },
      },
    },
    [WORK]: {
      on: {
        [ADD_PRIMING_STAGE_ACTION]: {
          target: PRIMING,
          actions: assign((context) => {
            console.log("going to piming stage");
            return {
              ...context,
              taskStages: [
                ...context.taskStages,
                { stageName: "priming", timeOfThoughts: [] },
              ],
            };
          }),
        },
        [GO_TO_RESULTS_ACTION]: {
          target: RESULTS,
          actions: assign((context, data) => {
            console.log("task status: ", data);
          }),
        },
        [ADD_THOUGHT_TIME_STAMP]: {
          actions: assign((context, { thoughtTime }) => {
            const lastStage =
              context.taskStages[context.taskStages.length - 1].timeOfThoughts;
            console.log(lastStage);
            lastStage.push(thoughtTime);
            return {
              ...context,
            };
          }),
        },
      },
    },
  },
});

Here is the code that doesn't work:

export const taskMachine = Machine({
  id: "taskMachineID",
  initial: CREATE,
  context: {
    taskName: "",
    taskStages: [],
  },
  states: {
    CREATE: {
      on: {
        [CREATE_NEW_TASK_ACTION]: {
          // CREATE_NEW TASK is an action.
          target: PRIMING,
          actions: assign((context, data) => {
            console.log("testing action works");
            return {
              ...context,
              taskName: data.taskName,
              taskStages: [{ stageName: "priming", timeOfThoughts: [] }],
            };
          }),
        },
      },
    },
    [PRIMING]: {
      on: {
        [ADD_WORK_STAGE_ACTION]: {
          target: WORK,
          actions: assign((context) => {
            return {
              ...context,
              taskStages: [
                ...context.taskStages,
                { stageName: "work", timeOfThoughts: [] },
              ],
            };
          }),
        },
        [ADD_THOUGHT_TIME_STAMP]: {
          actions: ["addThoughtTimStamp"],
        },
      },
    },
    [WORK]: {
      on: {
        [ADD_PRIMING_STAGE_ACTION]: {
          target: PRIMING,
          actions: assign((context) => {
            console.log("going to piming stage");
            return {
              ...context,
              taskStages: [
                ...context.taskStages,
                { stageName: "priming", timeOfThoughts: [] },
              ],
            };
          }),
        },
        [GO_TO_RESULTS_ACTION]: {
          target: RESULTS,
          actions: assign((context, data) => {
            console.log("task status: ", data);
          }),
        },
        [ADD_THOUGHT_TIME_STAMP]: {
          actions: ["addThoughtTimStamp"],
        },
      },
    },
    [RESULTS]: {},
  },
  actions: {
    addThoughtTimStamp: assign((context, { thoughtTime }) => {
      const lastStage =
        context.taskStages[context.taskStages.length - 1].timeOfThoughts;
      console.log(lastStage);
      lastStage.push(thoughtTime);
      return {
        ...context,
      };
    }),
  },
});

What am I missing?

P.S if you see other problems in my code feel free to comment, thanks.


Solution

  • In Machine constructor you have pass two arguments: config of your statechart and options for it, containing implementation of guards, actions, services and so on. In your "not working" example implementation goes into config, but should be inside options:

    let config = {
      id: "taskMachineID",
      initial: CREATE,
      context: {
        taskName: "",
        taskStages: [],
      },
      states: {
        CREATE: {
          on: {
            [CREATE_NEW_TASK_ACTION]: {
              // CREATE_NEW TASK is an action.
              target: PRIMING,
              actions: assign((context, data) => {
                console.log("testing action works");
                return {
                  ...context,
                  taskName: data.taskName,
                  taskStages: [{ stageName: "priming", timeOfThoughts: [] }],
                };
              }),
            },
          },
        },
        [PRIMING]: {
          on: {
            [ADD_WORK_STAGE_ACTION]: {
              target: WORK,
              actions: assign((context) => {
                return {
                  ...context,
                  taskStages: [
                    ...context.taskStages,
                    { stageName: "work", timeOfThoughts: [] },
                  ],
                };
              }),
            },
            [ADD_THOUGHT_TIME_STAMP]: {
              actions: ["addThoughtTimStamp"],
            },
          },
        },
        [WORK]: {
          on: {
            [ADD_PRIMING_STAGE_ACTION]: {
              target: PRIMING,
              actions: assign((context) => {
                console.log("going to piming stage");
                return {
                  ...context,
                  taskStages: [
                    ...context.taskStages,
                    { stageName: "priming", timeOfThoughts: [] },
                  ],
                };
              }),
            },
            [GO_TO_RESULTS_ACTION]: {
              target: RESULTS,
              actions: assign((context, data) => {
                console.log("task status: ", data);
              }),
            },
            [ADD_THOUGHT_TIME_STAMP]: {
              actions: ["addThoughtTimStamp"],
            },
          },
        },
        [RESULTS]: {},
      },
    };
    
    let options = {
      actions: {
        addThoughtTimStamp: assign((context, { thoughtTime }) => {
          const lastStage =
            context.taskStages[context.taskStages.length - 1].timeOfThoughts;
          console.log(lastStage);
          lastStage.push(thoughtTime);
          return {
            ...context,
          };
        }),
      },
    };
    
    Machine(config, options);
    
    

    Here is fixed example: https://codesandbox.io/s/eager-johnson-d5cj2?file=/src/index.js