# 状态

¥States

状态是系统(例如应用)在特定时间点的抽象表示。要了解更多信息,请阅读 状态图简介中有关状态的部分

¥A state is an abstract representation of a system (such as an application) at a specific point in time. To learn more, read the section on states in our introduction to statecharts.

# API

机器的当前状态由 State 实例表示:

¥The current state of a machine is represented by a State instance:













 
 
 
 
 
 


 
 
 
 
 
 

const lightMachine = createMachine({
  id: 'light',
  initial: 'green',
  states: {
    green: {
      /* ... */
    }
    // ...
  }
});

console.log(lightMachine.initialState);
// State {
//   value: 'green',
//   actions: [],
//   context: undefined,
//   // ...
// }

console.log(lightMachine.transition('yellow', { type: 'TIMER' }));
// State {
//   value: { red: 'walk' },
//   actions: [],
//   context: undefined,
//   // ...
// }

# 状态定义

¥State definition

State 对象实例是 JSON 可序列化的,并具有以下属性:

¥A State object instance is JSON-serializable and has the following properties:

  • value - 当前状态值(例如,{red: 'walk'}

    ¥value - the current state value (e.g., {red: 'walk'})

  • context - 该状态当前的 context

    ¥context - the current context of this state

  • event - 触发转换到此状态的事件对象

    ¥event - the event object that triggered the transition to this state

  • actions - 要执行的 actions 数组

    ¥actions - an array of actions to be executed

  • activities - 如果活动开始,则映射 activitiestrue;如果活动停止,则映射 false

    ¥activities - a mapping of activities to true if the activity started, or false if stopped.

  • history - 前一个 State 实例

    ¥history - the previous State instance

  • meta - 在 状态节点meta 属性上定义的任何静态元数据

    ¥meta - any static meta data defined on the meta property of the state node

  • done - 该状态是否表示最终状态

    ¥done - whether the state indicates a final state

State 对象还包含其他属性,例如 historyValueeventstree 以及其他通常不相关且在内部使用的属性。

¥The State object also contains other properties such as historyValue, events, tree and others that are generally not relevant and are used internally.

# 状态方法和属性

¥State methods and properties

你可以使用一些有用的方法和属性来获得更好的开发体验:

¥There are some helpful methods and properties you can use for a better development experience:

# state.matches(parentStateValue)

state.matches(parentStateValue) 方法确定当前 state.value 是否是给定 parentStateValue 的子集。该方法确定父状态值是否与状态值“匹配”。例如,假设当前 state.value{ red: 'stop' }

¥The state.matches(parentStateValue) method determines whether the current state.value is a subset of the given parentStateValue. The method determines if the parent state value “matches” the state value. For example, assuming the current state.value is { red: 'stop' }:

console.log(state.value);
// => { red: 'stop' }

console.log(state.matches('red'));
// => true

console.log(state.matches('red.stop'));
// => true

console.log(state.matches({ red: 'stop' }));
// => true

console.log(state.matches('green'));
// => false

提示

如果要匹配多个状态之一,可以在状态值数组上使用 .some() (opens new window) 来完成此操作:

¥If you want to match one of multiple states, you can use .some() (opens new window) on an array of state values to accomplish this:

const isMatch = [{ customer: 'deposit' }, { customer: 'withdrawal' }].some(
  state.matches
);

# state.nextEvents

state.nextEvents 指定将导致从当前状态转换的下一个事件:

¥state.nextEvents specifies the next events that will cause a transition from the current state:

const { initialState } = lightMachine;

console.log(initialState.nextEvents);
// => ['TIMER', 'EMERGENCY']

state.nextEvents 对于确定可以采取哪些下一个事件以及在 UI 中表示这些潜在事件(例如启用/禁用某些按钮)非常有用。

¥state.nextEvents is useful in determining which next events can be taken, and representing these potential events in the UI such as enabling/disabling certain buttons.

# state.changed

state.changed 指定此 state 是否已从先前状态更改。如果出现以下情况,则状态被视为“已更改”:

¥state.changed specifies if this state has changed from the previous state. A state is considered “changed” if:

  • 它的值不等于它之前的值,或者:

    ¥Its value is not equal to its previous value, or:

  • 它有任何新的操作(副作用)要执行。

    ¥It has any new actions (side-effects) to execute.

初始状态(没有历史记录)将返回 undefined

¥An initial state (with no history) will return undefined.

const { initialState } = lightMachine;

console.log(initialState.changed);
// => undefined

const nextState = lightMachine.transition(initialState, { type: 'TIMER' });

console.log(nextState.changed);
// => true

const unchangedState = lightMachine.transition(nextState, {
  type: 'UNKNOWN_EVENT'
});

console.log(unchangedState.changed);
// => false

# state.done

state.done 指定 state 是否是 “最终状态” - 最终状态是指示其机器已达到其最终(终端)状态并且不能再转换到任何其他状态的状态。

¥state.done specifies whether the state is a “final state” - a final state is a state that indicates that its machine has reached its final (terminal) state and can no longer transition to any other state.

const answeringMachine = createMachine({
  initial: 'unanswered',
  states: {
    unanswered: {
      on: {
        ANSWER: { target: 'answered' }
      }
    },
    answered: {
      type: 'final'
    }
  }
});

const { initialState } = answeringMachine;
initialState.done; // false

const answeredState = answeringMachine.transition(initialState, {
  type: 'ANSWER'
});
answeredState.done; // true

# state.toStrings()

state.toStrings() 方法返回表示所有状态值路径的字符串数组。例如,假设当前 state.value{ red: 'stop' }

¥The state.toStrings() method returns an array of strings that represent all of the state value paths. For example, assuming the current state.value is { red: 'stop' }:

console.log(state.value);
// => { red: 'stop' }

console.log(state.toStrings());
// => ['red', 'red.stop']

state.toStrings() 方法对于表示基于字符串的环境中的当前状态非常有用,例如在 CSS 类或数据属性中。

¥The state.toStrings() method is useful for representing the current state in string-based environments, such as in CSS classes or data-attributes.

# state.children

state.children 是将生成的服务/参与者 ID 映射到其实例的对象。详细信息请参见 📖 参考服务

¥state.children is an object mapping spawned service/actor IDs to their instances. See 📖 Referencing Services for more details.

# 使用 state.children 的示例

¥Example using state.children

const machine = createMachine({
  // ...
  invoke: [
    { id: 'notifier', src: createNotifier },
    { id: 'logger', src: createLogger }
  ]
  // ...
});

const service = invoke(machine)
  .onTransition((state) => {
    state.children.notifier; // service from createNotifier()
    state.children.logger; // service from createLogger()
  })
  .start();

# state.hasTag(tag)

自 4.19.0 起

¥Since 4.19.0

state.hasTag(tag) 方法确定当前状态配置是否具有带有给定标记的状态节点。

¥The state.hasTag(tag) method determines whether the current state configuration has a state node with the given tag.





 


 


 




const machine = createMachine({
  initial: 'green',
  states: {
    green: {
      tags: 'go' // single tag
    },
    yellow: {
      tags: 'go'
    },
    red: {
      tags: ['stop', 'other'] // multiple tags
    }
  }
});

例如,如果上面的机器处于 greenyellow 状态,则可以使用 state.hasTag('go') 来代替直接使用 state.matches('green') || state.matches('yellow') 来匹配状态:

¥For instance, if the above machine is in the green or yellow state, instead of matching the state directly using state.matches('green') || state.matches('yellow'), it is possible to use state.hasTag('go'):

const canGo = state.hasTag('go');
// => `true` if in 'green' or 'yellow' state

# state.can(event)

从 4.25.0 开始

¥Since 4.25.0

state.can(event) 方法确定 event 如果发送到解释机是否会导致状态更改。如果发送 event 导致状态发生变化,该方法将返回 true;否则该方法将返回 false

¥The state.can(event) method determines whether an event will cause a state change if sent to the interpreted machine. The method will return true if the state will change due to the event being sent; otherwise the method will return false:

const machine = createMachine({
  initial: 'inactive',
  states: {
    inactive: {
      on: {
        TOGGLE: 'active'
      }
    },
    active: {
      on: {
        DO_SOMETHING: { actions: ['something'] }
      }
    }
  }
});

const inactiveState = machine.initialState;

inactiveState.can({ type: 'TOGGLE' }); // true
inactiveState.can({ type: 'DO_SOMETHING' }); // false

inactiveState.can({
  type: 'DO_SOMETHING',
  data: 42
}); // false

const activeState = machine.transition(inactiveState, { type: 'TOGGLE' });

activeState.can({ type: 'TOGGLE' }); // false
activeState.can({ type: 'DO_SOMETHING' }); // true, since an action will be executed

如果 state.changedtrue 并且满足以下任一条件,则状态被视为“已更改”:

¥A state is considered “changed” if state.changed is true and if any of the following are true:

  • 它的 state.value 变化

    ¥its state.value changes

  • 有新的 state.actions 要执行

    ¥there are new state.actions to be executed

  • 它的 state.context 发生了变化。

    ¥its state.context changes.

警告

state.can(...) 函数还将通过执行转换防护来检查它们。转换守卫应该是纯函数。

¥The state.can(...) function will also check transition guards by executing them. Transition guards should be pure functions.

# 持续状态

¥Persisting state

如前所述,可以通过将 State 对象序列化为字符串 JSON 格式来持久保存该对象:

¥As mentioned, a State object can be persisted by serializing it to a string JSON format:

const jsonState = JSON.stringify(currentState);

// Example: persisting to localStorage
try {
  localStorage.setItem('app-state', jsonState);
} catch (e) {
  // unable to save to localStorage
}

可以使用静态 State.create(...) 方法恢复状态:

¥State can be restored using the static State.create(...) method:

import { State, interpret } from 'xstate';
import { myMachine } from '../path/to/myMachine';

// Retrieving the state definition from localStorage, if localStorage is empty use machine initial state
const stateDefinition =
  JSON.parse(localStorage.getItem('app-state')) || myMachine.initialState;

// Use State.create() to restore state from a plain object
const previousState = State.create(stateDefinition);

然后,你可以通过将 State 传递到解释服务的 .start(...) 方法来从此状态解释机器:

¥You can then interpret the machine from this state by passing the State into the .start(...) method of the interpreted service:

// ...

// This will start the service at the specified State
const service = interpret(myMachine).start(previousState);

这还将维护和恢复以前的 历史陈述 并确保 .events.nextEvents 代表正确的值。

¥This will also maintain and restore previous history states and ensures that .events and .nextEvents represent the correct values.

警告

XState 尚不支持持久生成 actors

¥Persisting spawned actors isn't yet supported in XState.

# 状态元数据

¥State meta data

元数据是描述任何 状态节点 相关属性的静态数据,可以在状态节点的 .meta 属性上指定:

¥Meta data, which is static data that describes relevant properties of any state node, can be specified on the .meta property of the state node:

















 
 
 


 
 
 





 
 
 


 
 
 


 
 
 




const fetchMachine = createMachine({
  id: 'fetch',
  initial: 'idle',
  states: {
    idle: {
      on: { FETCH: { target: 'loading' } }
    },
    loading: {
      after: {
        3000: 'failure.timeout'
      },
      on: {
        RESOLVE: { target: 'success' },
        REJECT: { target: 'failure' },
        TIMEOUT: { target: 'failure.timeout' } // manual timeout
      },
      meta: {
        message: 'Loading...'
      }
    },
    success: {
      meta: {
        message: 'The request succeeded!'
      }
    },
    failure: {
      initial: 'rejection',
      states: {
        rejection: {
          meta: {
            message: 'The request failed.'
          }
        },
        timeout: {
          meta: {
            message: 'The request timed out.'
          }
        }
      },
      meta: {
        alert: 'Uh oh.'
      }
    }
  }
});

机器的当前状态收集所有状态节点的 .meta 数据,用状态值表示,并将它们放在一个对象上,其中:

¥The current state of the machine collects the .meta data of all of the state nodes, represented by the state value, and places them on an object where:

例如,如果上述机器处于 failure.timeout 状态(由 ID 为 "failure""failure.timeout" 的两个状态节点表示),则 .meta 属性将组合所有 .meta 值,如下所示:

¥For instance, if the above machine is in the failure.timeout state (which is represented by two state nodes with IDs "failure" and "failure.timeout"), the .meta property will combine all .meta values as follows:




 
 
 
 
 
 
 
 



const failureTimeoutState = fetchMachine.transition('loading', {
  type: 'TIMEOUT'
});

console.log(failureTimeoutState.meta);
// => {
//   failure: {
//     alert: 'Uh oh.'
//   },
//   'failure.timeout': {
//     message: 'The request timed out.'
//   }
// }

TIP:聚合元数据

你如何处理元数据取决于你。理想情况下,元数据应仅包含 JSON 可序列化值。考虑以不同的方式合并/聚合元数据。例如,以下函数丢弃状态节点 ID 键(如果它们不相关)并合并元数据:

¥What you do with meta data is up to you. Ideally, meta data should contain JSON-serializable values only. Consider merging/aggregating the meta data differently. For example, the following function discards the state node ID keys (if they are irrelevant) and merges the meta data:

function mergeMeta(meta) {
  return Object.keys(meta).reduce((acc, key) => {
    const value = meta[key];

    // Assuming each meta value is an object
    Object.assign(acc, value);

    return acc;
  }, {});
}

const failureTimeoutState = fetchMachine.transition('loading', {
  type: 'TIMEOUT'
});

console.log(mergeMeta(failureTimeoutState.meta));
// => {
//   alert: 'Uh oh.',
//   message: 'The request timed out.'
// }

# 注意

¥Notes

  • 你永远不必手动创建 State 实例。将 State 视为仅来自 machine.transition(...)service.onTransition(...) 的只读对象。

    ¥You should never have to create a State instance manually. Treat State as a read-only object that only comes from machine.transition(...) or service.onTransition(...).

  • state.history 不会保留其历史记录以防止内存泄漏。state.history.history === undefined。否则,你最终会创建一个巨大的链表并重新发明区块链,这是我们不关心的。

    ¥state.history will not retain its history in order to prevent memory leaks. state.history.history === undefined. Otherwise, you end up creating a huge linked list and reinventing blockchain, which we don't care to do.

    • 此行为可能会在未来版本中进行配置。

      ¥This behavior may be configurable in future versions.