为什么这个赋值不触发?

Why this assign does not trigger?

看完official xstate tutorial, I tried to implement my own machine inspired by this post on dev.to by one of the xstate's dev.

除了output似乎没有更新外,一切都按预期进行。我认为这项任务没有完成它的工作。我忘记了什么?

为了进行比较,这是来自 xstate 的工作 demo,其中上下文中的变量按预期更新。

有关 Context | XState Docs

分配的更多信息

我的代码:

import "./styles.css";
import * as React from "react";
import * as ReactDOM from "react-dom";
import { createMachine, assign } from "xstate";
import { useMachine } from "@xstate/react";

interface FetchContext {
  output: string;
}

const fetchifetch = async () => {
  return await fetch(
    "https://jsonplaceholder.typicode.com/todos/1"
  ).then((response) => response.json());
};

const fetchMachine = createMachine<FetchContext>({
  initial: "idle",
  context: {
    output: "wesh" // none selected
  },
  states: {
    idle: {
      on: {
        FETCH: "loading"
      }
    },
    loading: {
      invoke: {
        src: (context, event) => async (send) => {
          setTimeout(async () => {
            const data = await fetchifetch();
            console.log("done");
            console.log(data);
            // well. here I want to assign something to output
            assign({
              output: (context, event) => data.title
            });
            send("FETCHED_SUCCESSFULLY");
          }, 4000);
          console.log("start");
        },
        onError: {
          target: "idle"
        }
      },
      on: {
        FETCHED_SUCCESSFULLY: {
          target: "idle"
        }
      }
    },
    fetch: {
      on: {
        CLOSE: "idle"
      }
    }
  }
});

function App() {
  const [current, send] = useMachine(fetchMachine);
  const { output } = current.context;

  return (
    <div className="App">
      <h1>XState React Template</h1>
      <br />
      <input
        disabled={current.matches("loading")}
        defaultValue="yo"
        onChange={(e) => console.log(e.currentTarget.value)}
      />
      <button
        disabled={current.matches("loading")}
        onClick={() => send("FETCH")}
      >
        click to fetch
      </button>

      <!-- let's display the result over here -->
      <div>{output}</div>
      <div>{current.context.output}</div>
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

然后您需要 return 一个 Promise,并在 Promise 解决后让状态机更新上下文。

上下文在 invokeonDone 属性 中更新。

const fetchMachine = createMachine<FetchContext>({
  initial: "idle",
  context: {
    output: "wesh" // none selected
  },
  states: {
    idle: {
      on: {
        FETCH: "loading"
      }
    },
    loading: {
      invoke: {
        src: (context, event) => async (send) => {
          return new Promise((resolve, reject) => {
            setTimeout(async () => {
              try {
                const data = await fetchifetch();
                resolve(data.title);
              } catch (err) {
                 reject(err);
              }
            }, 4000);
          });
        },
        onDone: {
          target: "fetch",
          actions: assign({
            output: (_, event) => event.data,
          })
        },
        onError: {
          target: "idle"
        }
      },
      on: {
        FETCHED_SUCCESSFULLY: {
          target: "idle",
        }
      }
    },
    fetch: {
      on: {
        CLOSE: "idle"
      }
    }
  }
});