XState react - 跨多个组件共享机器实例

Posted

技术标签:

【中文标题】XState react - 跨多个组件共享机器实例【英文标题】:XState react - share machine instance across multiple components 【发布时间】:2020-03-28 04:08:35 【问题描述】:

我有两台机器:AuthenticationMachineAddressMachine。 AuthenticationMachine 由 Login component 使用,而 AddressMachine 由 Address component 使用。但是我需要地址组件中来自AuthenticationMachine 的token 才能与AddressMachine 一起使用。我尝试在 AuthenticationMachine 中生成 Actor,但没有成功。

身份验证机器

const createService = (context: token: string) => 
  return Machine(
    id: 'service',
    initial: 'logged_in',
    states: 
      logged_in: ,
      logged_out: ,
    ,
  ).withContext(context);
;

const authenticationMachine = Machine(
  
    id: 'authenticationMachine',
    initial: 'unauthenticated',
    context: 
      token: undefined,
      error: undefined,
      service: undefined,
    ,
    states: 
      unauthenticated: 
        on: 
          LOGIN: 'authenticating',
        ,
      ,
      authenticating: 
        invoke: 
          src: 'login',
          onDone: 
            target: 'loggedIn',
            actions: assign(
              token: (ctx, event: any) => event.data.access_token,
              service: (ctx, event) => 
                return spawn(createService(token: event.data.access_token));
              ,
            ),
          ,
          onError: 
            target: 'unauthenticated',
            actions: assign(
              error: (ctx, event: any) => event.data,
            ),
          ,
        ,
      ,
      loggedIn: 
        on: 
          LOGOUT: 'unauthenticated'
        ,
      ,
    ,
  ,
);

【问题讨论】:

【参考方案1】:

我认为这可能是您对 assign 的使用,或者只是您实现组件的方式有问题。

这是您的机器在 codesandbox 中的组件的工作示例

另外,还有一个简单的例子,说明如何在组件中使用机器:

地址组件:

function Address(props) 
  const [current] = useService(props.service);
  return <>Address Component token: current.context.token</>;

登录组件:

export default function Login() 
  const [current, send] = useMachine(authenticationMachine);
  const isUnauthenticated = current.matches("unauthenticated");
  const isAuthenticating = current.matches("authenticating");
  const isLoggedIn = current.matches("loggedIn");

  if (isUnauthenticated) 
    return (
      <>
        token: current.context.token
        <br />
        <button onClick=() => send("LOGIN")>Login</button>
      </>
    );
  
  if (isAuthenticating) 
    return <>...hold on, authenticating</>;
  
  if (isLoggedIn) 
    return (
      <>
        token: current.context.token
        <br />
        <button onClick=() => send("LOGOUT")>Logout</button>
        <br />
        <Address service=current.context.service />
      </>
    );
  

您的机器示例已扩展:

import  assign, Machine, spawn  from "xstate";

const createService = (context:  token: string ) => 
    return Machine(
        id: 'service',
        initial: 'logged_in',
        states: 
            logged_in: ,
            logged_out: ,
        ,
    ).withContext(context);
;

const login = (ctx) => 
    const  email, pw  = ctx;
    return new Promise((resolve, reject) => 
        if(email === 'a@b.c' && pw === 'abc') 
            return resolve( token: 'jahsfjkhakjsnfj');
         else 
            return reject( token: undefined );
        
    );
;

const authenticationMachine = Machine(
    
        id: 'authenticationMachine',
        initial: 'unauthenticated',
        context: 
            token: undefined,
            error: undefined,
            service: undefined,
            email: 'a@b.c',
            pw: 'abc'
        ,
        states: 
            unauthenticated: 
                on: 
                    LOGIN: 'authenticating',
                ,
            ,
            authenticating: 
                invoke: 
                    id: 'login',
                    src: context => login(context),
                    onDone: 
                        target: 'loggedIn',
                        actions: [
                            assign( token: (_, event) => event.data.token ),
                            assign( service: (_, event) => 
                                return spawn(createService(token: event.data.token));
                            )
                        ]
                    ,
                    onError:  target: 'unauthenticated' ,
                ,
            ,
            loggedIn: 
                on: 
                    LOGOUT: 'unauthenticated'
                ,
            ,
        ,
    
);

export  authenticationMachine ;

【讨论】:

我考虑过使用这种方法来解决问题。我已经在一个 react 项目中尝试过这种方法并且效果很好,但是我使用的是 react native 和 react 导航。我不能传递道具。 如何使用 screenProps 像这篇文章中详述的那样,medium.com/@benfox_63249/… ? 我无法提供更多帮助,因为我没有真正使用 react-native 的经验 反应导航路由器不像你在网络上那样接受子路由,它提供了一个函数,例如返回的 createStackNavigator 函数 - 我猜是一个组件。我尝试了一些我在网上找到的不同方法来传递 screenProps,但没有添加道具。

以上是关于XState react - 跨多个组件共享机器实例的主要内容,如果未能解决你的问题,请参考以下文章

存储在 XSTATE 中以便跨组件访问?

有没有办法在 useEffect 的反应组件中设置 xstate 机器的状态?

Apollo GraphQL (React) 跨组件数据共享

我们如何跨存储库共享 React 组件

React Custom Hooks 全局获取数据并跨组件共享?

跨多个 React 应用共享 redux 服务层