结合 useMachine 和 useContext

问题描述

我正在开发一个 UI 项目,该项目通过共享上下文处理状态更新,非常类似于 here

const {appState,dispatch} = useContext(AppContext);

我不会通过 xstate 来处理某些组件的状态机。

const SomeComponent = () => {
  const {appState,dispatch} = useContext(AppContext);
  const [state,send,service] = useMachine(MyComponentStateMachine);
}

现在,理想情况下,我希望我的状态机在进入状态时分派某些事件。但是,状态机获取我的 AppContext 的最佳方式是什么?

目前,我正在组件本身上处理这个事件调度,观察状态机的状态并在它进入某个状态时根据需要调度一个事件:

const SomeComponent = () => {
  const {appState,service] = useMachine(MyComponentStateMachine);

  useEffect(() => service.subscribe(state => {
     if(state.value == "Some relevant state of MyComponentStateMachine")
       dispatch({type: SomeEvent,arg: 12345});  
    }).unsubscribe,[service]);
}

这很管用,但我觉得它的设计很糟糕。我认为直接从状态机而不是从组件调度这个事件会更清晰。

状态机有什么好的方法获取 AppContext 吗?

简单地为状态机创建一个工厂方法,将 dispatch 作为参数并保留它是否明智?

解决方法

我相信在那里调用你的调度函数没有错。由于您正在使用上下文,您将无法在机器内部调用调度,除非您将该函数作为参数传递。你可以试试,不知道行不行。

(您可以使用操作来触发事件或状态变化的副作用)

在那种情况下,它会是这样的:

<pre>
  //Component
  const SomeComponent = () => {
    const { appState,dispatch } = useContext(AppContext);
    const [ state,send ] = useMachine(MyComponentStateMachine);

    useEffect(() => {
      if(state.matches("stateName")) {
        const data = { type: SomeEvent,arg: 12345 };
        send("EVENT_NAME",{ callback: dispatch,data })
      }
    },[state]);
  }


  //Machine
  const MyComponentStateMachine = Machine({
    ...
    states: {
      stateName: {
        on: {
          EVENT_NAME: "secondState",},secondState: {
        entry: ["actionName"]
      }
    },{
      actions: {
        actionName: (ctx,e) => {
          e.callback(e.data);
        },}
    }
  });
</pre>

*** 另外看看我是如何比较状态的,这是一种更清晰的读取状态值的方法。此外,如果您已经在使用 useMachine 钩子,则无需订阅该服务,如果状态发生变化,该钩子将触发组件重新渲染。