XState React - 未触发调用服务

XState React - Invoke Service not triggered

我正在使用 xstatereact 来实现基本的登录功能。代码是 here,我面临的问题是,在事件 AUTHENTICATING 中,它旨在调用服务 authenticateUser 但它没有调用。控制台中没有可见的错误。该组件看起来像

import { useMachine } from "@xstate/react";
import { createMachine, assign } from "xstate";
import "./App.css";

const authenticateUserNew = async (c, e) => {
  console.log("service invoked");
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (Math.random() > 0.5) {
        resolve();
      } else {
        reject();
      }
    }, 1000);
  });
};

const loginMachine = createMachine(
  {
    id: "login-machine",
    initial: "unauthenticated",
    context: {
      isAuthenticated: false,
    },
    states: {
      unauthenticated: {
        on: {
          AUTHENTICATING: {
            invoke: {
              id: "authenticateUser",
              src: (c, e) => authenticateUserNew(c, e),
              onDone: {
                target: "authenticated",
                actions: assign({ isAuthenticated: (context, event) => true }),
              },
              onError: {},
            },
          },
        },
      },
      authenticated: {
        on: {
          LOGOUT: {
            target: "unauthenticated",
          },
        },
      },
    },
  },
  {
    services: {
      authenticateUser: () => {
        console.log("service invoked");
        return new Promise((resolve, reject) => {
          setTimeout(() => {
            if (Math.random() > 0.5) {
              resolve();
            } else {
              reject();
            }
          }, 1000);
        });
      },
    },
  }
);

function App() {
  const [current, send] = useMachine(loginMachine);
  return (
    <div className="App">
      <h2>{current.value}</h2>
      <br />
      <h3>
        isAuthenticated: {current.context.isAuthenticated ? "True" : "False"}
      </h3>
      <br />
      <button onClick={() => send("AUTHENTICATING")}>AUTHENTICATE</button>
      <br />
      <button onClick={() => send("LOGOUT")}>LOGOUT</button>
    </div>
  );
}

export default App;

我已经尝试了两种方法,我可以将函数外部化并使用它或在状态机的 service 部分定义它,在这两种情况下它都没有被调用。

第一种方法

invoke: {
  id: "authenticateUser",
  src: (c, e) => authenticateUserNew(c, e),
  onDone: {
    target: "authenticated",
    actions: assign({ isAuthenticated: (context, event) => true }),
  },
  onError: {},
}

第二种方法

invoke: {
  id: "authenticateUser",
  src: "authenticateUser",
  onDone: {
    target: "authenticated",
    actions: assign({ isAuthenticated: (context, event) => true }),
  },
  onError: {},
}

React version: ^17.0.2 xstate: ^4.3.5 @xstate/react: 2.0.1

来自docs

An invocation is defined in a state node's configuration with the invoke property

您正在尝试在 事件节点 中调用,而不是在状态节点中调用。

例如,您可以这样做:

...
    states: {
      unauthenticated: {
        on: {
          AUTHENTICATE: {
            target: 'authenticating'
          },
        },
      },
      authenticating: {
        invoke: {
          id: "authenticateUser",
          src: 'authenticateUser',
          onDone: {
            target: "authenticated",
            actions: assign({ isAuthenticated: (context, event) => true }),
          },
          onError: {
            target: 'unauthenticated'
          },
        },
      },
      authenticated: {
        on: {
          LOGOUT: {
            target: "unauthenticated",
          },
        },
      },
    },
...

并发送 AUTHENTICATE 事件:

<button onClick={() => send("AUTHENTICATE")}>AUTHENTICATE</button>

此外,我建议完全避免 isAuthenticated。您可以检查您是否通过了 matches method:

的身份验证
<h3>
    isAuthenticated: {current.matches('authenticated') ? "True" : "False"}
</h3>