xstate - 如何正确处理转换错误?

xstate - how to properly handle transitions errors?

我是 Xstate 的新手,我正在努力寻找官方文档中的帮助。

问题很简单,我想知道在不应该触发的情况下是否触发了事件。

我有一个在转换方面非常严格的基本工作流程,例如,我的状态不能从 'pending' 转到 'processed' 而不从 'uploaded' 传递。

如果我使用:

stateService.send('PROCESSED') 

当状态处于 'pending' 时,状态不会改变(正确)但是 Xstate 中是否有任何实用程序或事件实际上告诉我交易没有被触发,因为事件不是 allowed/correct?

这是我的状态

const stateMachine = Machine(
  {
    id: 'vocalTrack',
    initial: 'PENDING',
    context: {},
    states: {
      PENDING: {
        on: {
          UPLOADED: 'UPLOADED',
        },
      },
      UPLOADED: {
        on: {
          PROCESSED: 'PROCESSED',
        },
        entry: 'onUploaded',
      },
      PROCESSED: {
        on: {
          READY: 'READY',
          ERROR: 'ERROR',
        },
        exit: 'onProcessed',
      },
      READY: {
        type: 'final',
      },
      ERROR: {
        on: {
          UPLOADED: 'UPLOADED',
        },
        type: 'final',
      },
    },
  },
  {
    actions: {
      onUploaded: (context, event) => {
        console.log(`++++ onUploaded action: ++++`)
      },
      onProcessed: (context, event) => {
        console.log(`++++ onProcessed action: ++++`)
      },
    },
  },
)

const stateService = interpret(stateMachine)
stateService.init('PENDING')

// I'd like to catch the following workflow errors
stateService.send('UPLOADED')
stateService.send('PROCESSED')

没有任何选项可以传递给机器或解释调用,这将为您提供 "free"。不过,您可以轻松地向 "onTransition" 方法添加一些逻辑:

选项 1,在 state.changed 上验证:

    service.onTransition(state => {
        if (state.changed) {
            console.log(`The state did change, so ${state.event.type} caused a valid transition`);
        } else {
            console.log(`${state.event.type} did not cause a state change`);
        }
    })

选项 2,根据 state.value & 事件名称进行验证:

    service.onTransition(state => {
        if (state.event.type === 'xstate.init'){
            console.log('Init Transition');
            return;
        }
        if(state.value === state.event.type){
            console.log('Transition Valid');
        }else {
            console.log('Transition Invalid');
        }
    })

选项 1 可能是首选解决方案!您可以阅读更多相关内容 here in the documentation. The following 也与根本不存在的事件有关。

我在 React 中创建了一个小型演示应用程序来演示这一点:

这是一个已解决的问题,但我相信还有一个不同的问题,可以在机器配置级别上处理它。所以我会 post 比较一下。

考虑到:

您可以在整机配置级别定义PROCESSED事件:

const stateMachine = Machine(
    {
        id: "vocalTrack",
        initial: "PENDING",
        context: {},
        on: {
            PROCESSED: {
                actions: ["invalidCall"]
            }
        },
        states: {
            PENDING: {
                on: {
                    UPLOADED: "UPLOADED"
                }
            },
            UPLOADED: {
                on: {
                    PROCESSED: "PROCESSED"
                },
                entry: "onUploaded"
            },
            ...
        },
    }, {
        actions: {
            ...
            invalidCall: (context, event, meta) => {
                console.log(
                `invalid call of event ${event.type} on the ${meta.state.value} state`
                );
            }
        }
    }
);

实际上,在每个未处理 PROCESSED 事件的状态下,它将传播到主要配置级别。

Example on codesandbox

更重要的是,wildcard descriptor exists 将捕获所有未在给定级别定义的事件,因此我们不妨捕获给定状态下不支持的所有触发事件:

on: {
    "*": {
        actions: ["invalidCall"]
    }
},

当然,这完全取决于您的具体情况,但我喜欢该解决方案的一点是,它都停留在机器定义级别。

我还设法通过以下方式实现此功能:

export const stateMachine = createMachine({
  id: 'stateMachine',
  initial: 'created',
  strict: true,
  on: {
    '*': {
      cond: (_context, _event, meta) => {
        throw new Error(`Cannot change from ${meta.state.value}`);
      },
    },
  },
  states: {
    created: {
      on: {