如何防止 XState 节点在接收事件时重置其子并行状态?

Posted

技术标签:

【中文标题】如何防止 XState 节点在接收事件时重置其子并行状态?【英文标题】:How can I prevent XState node from resetting it's child parallel states when receiving an event? 【发布时间】:2021-10-03 07:16:22 【问题描述】:

我正在探索 XState 世界,并尝试重新创建 @davidkpiano 中提到的机器 this talk

并且遇到了正确进行转换的问题。当我向父机器发送消息时,它会重置所有子机器。

例如:我希望机器在我触发 CHANGEFOCUS 后处于 dirtyfocused 状态强>事件一个接一个。但在发送 FOCUS 消息后,原始状态会重置回 原始

我找到了this issue,但多个转换做同样的事情(正如它在问题本身中实际描述的那样)

另外,我不想将所有关于原始、聚焦和触摸状态的信息存储到上下文中,因为这样做并不安全使用状态机。

下面的代码可以复制粘贴到https://xstate.js.org/viz/

const createPristineMachineConfig = (inputName) => 
  return 
    id: `$inputName.pristine`,
    initial: 'pristine',
    states: 
      pristine: 
        on: 
          [`$inputName.CHANGE`]: 'dirty',
        ,
      ,
      dirty: 
        type: 'final',
      ,
    ,
  ;
;

const createTouchedConfig = (inputName) => 
  return 
    id: `$inputName.touched`,
    initial: 'untouched',
    states: 
      untouched: 
        on: 
          [`$inputName.TOUCH`]: 'touched',
        ,
      ,
      touched: 
        type: 'final',
      ,
    ,
  ;
;

const createFocusedMachineConfig = (inputName) => 
  return 
    id: `$inputName.focused`,
    initial: 'blurred',
    states: 
      blurred: 
        on: 
          [`$inputName.FOCUS`]: 'focused',
        ,
      ,
      focused: 
        on: 
          [`$inputName.BLUR`]: 'blurred',
        ,
      ,
    ,
  ;
;

const createInputMachineConfig = (inputName) => (
  id: inputName,
  type: 'parallel',
  context: 
    value: '',
  ,
  on: 
    FOCUS: 
      actions: send(`$inputName.FOCUS`),
      internal: true,
    ,
    BLUR: 
      actions: [send(`$inputName.TOUCH`), send(`$inputName.BLUR`)],
      internal: true,
    ,
    CHANGE: 
      actions: [assign((ctx, event) => ( ...ctx, value: event.payload.value )), send(`$inputName.CHANGE`)],
      internal: true,
    ,
  ,
  states: 
    pristine: createPristineMachineConfig(inputName),
    touched: createTouchedConfig(inputName),
    focused: createFocusedMachineConfig(inputName),
  ,
);

const loginInputMachine = Machine(createInputMachineConfig('login'));

【问题讨论】:

自己无法解决,但您可以尝试以下一些想法:正如您在 GH 问题中已经看到的那样,当整个机器收到一个事件。你可以试试历史状态。也许尝试直接指定目标状态而不是使用send() 函数。对机器和子状态的 id 使用动态值对我来说似乎有点过分。您可以通过使用硬编码值来简化它。事件可能会被意外发送到整个机器而不是子状态;不过不确定。 【参考方案1】:

这可能是因为dirty 是一个最终状态,例如:


  id: 'loginForm',
  initial: 'idle',
  context: 
    error: undefined,
  ,
  invoke: [
    
      id: 'usernameInput',
      src: inputMachine,
    ,
    
      id: 'passwordInput',
      src: inputMachine,
    ,
    
      id: 'loginButton',
      src: buttonMachine,
    ,
  ],
  states: 
    idle: 
    ,
    processing: 

    ,
    finished: 
    ,
    failed: 
    ,
  ,
;

我有这台机器,它产生了一些演员,我可以通过loginForm 机器以下列方式访问loginForm.state.children.usernamInput,但如果其中一台机器达到最终状态,它将返回未定义。

【讨论】:

以上是关于如何防止 XState 节点在接收事件时重置其子并行状态?的主要内容,如果未能解决你的问题,请参考以下文章

如何在首次显示时折叠包含所有节点的 orgChart,然后通过选择事件显示其子级的第一级?

在允许孩子捕捉触摸的同时防止父母被点击

如何使用 xstate 跟踪分析事件

使用 xstate,是不是可以配置一个适用于所有状态并在所有状态和子状态中以相同方式处理的事件?

Android LiveData防止在观察时接收到最后一个值

TVirtualStringTree。如何通过一次确认检查节点及其子节点?