XState React - 调用服务未触发

发布于 2025-01-12 07:55:25 字数 2977 浏览 3 评论 0原文

我使用 xstatereact 来实现基本的登录功能。代码位于此处,我面临的问题是,关于事件 AUTHENTICATING< /code> 它的目的是调用一个服务 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版本:^17.0.2 xstate:^4.3.5 @xstate/react: 2.0.1

I am using xstate with react to implement a basic login functionality. The code is here and the issue I am facing is, on the event AUTHENTICATING it is meant to invoke a service authenticateUser and it is not invoking. No visible errors in the console. The component looks like

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;

I have tried both the approach where I can externalize function and use it or define it inside the service section of state machine, in both the case it wasn't invoked.

1st approach

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

2nd approach

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

甜点 2025-01-19 07:55:25

来自文档

调用是通过调用属性在状态节点的配置中定义的

而您尝试在事件节点< /em>,不是状态一。

例如,您可以这样做:

...
    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 。您可以检查您是否已通过 匹配项进行身份验证方法:

<h3>
    isAuthenticated: {current.matches('authenticated') ? "True" : "False"}
</h3>

From docs:

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

You are instead trying to invoke in an event node, not a state one.

For example, you could do:

...
    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",
          },
        },
      },
    },
...

and send the AUTHENTICATE event:

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

Moreover, I'd like to suggest to avoid the isAuthenticated at all. You can check if you're authenticated with the matches method:

<h3>
    isAuthenticated: {current.matches('authenticated') ? "True" : "False"}
</h3>
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文