# @xstate/graph

@xstate/graph package (opens new window) 包含 XState 机的图形算法和实用程序。

¥The @xstate/graph package (opens new window) contains graph algorithms and utilities for XState machines.

# 快速开始

¥Quick Start

  1. 安装 xstate@xstate/graph

    ¥Install xstate and @xstate/graph:

npm install xstate @xstate/graph
  1. 导入图形实用程序。示例:

    ¥Import the graph utilities. Example:

import { createMachine } from 'xstate';
import { getSimplePaths } from '@xstate/graph';

const machine = createMachine(/* ... */);
const paths = getSimplePaths(machine);

# API

# getShortestPaths(machine, options?)

参数

¥Arguments

machine (opens new window)最短路径(Dijkstra 算法) (opens new window) 从初始状态返回到每个其他状态作为映射对象,其中:

¥Returns the shortest paths (Dijkstra's algorithm) (opens new window) of a machine (opens new window) from the initial state to every other state as a mapped object, where the:

  • key 是字符串化状态

    ¥key is the stringified state

  • value 是一个具有以下属性的对象:

    ¥value is an object with the properties:

path 是一个段数组,其中每个段都是一个具有以下属性的对象:

¥The path is an array of segments, where each segment is an object with the properties:

  • state - 该段的 State (opens new window)

    ¥state - the State (opens new window) of the segment

  • weight - 路径总 weight (opens new window)

    ¥weight - the total weight (opens new window) of the path

    • 目前,从一种状态到另一种状态的每次转换的权重均为 1。将来这将是可定制的。

      ¥Currently, each transition from one state to another has a weight of 1. This will be customizable in the future.

  • event - 将 machine 从路径中的状态转换到下一个状态的事件对象

    ¥event - the event object that transitions the machine from the state to the next state in the path

每条路径都从初始状态开始。

¥Every path starts with the initial state.

整体对象结构如下所示:

¥The overall object structure looks like this:

{
  "<SERIALIZED STATE>": {
    "state": State { ... },
    "path": [
      {
        "state": State { ... },
        "event": { "type": "<event.type>", "<PROP>": "<event.PROP>" }
      },
      {
        "state": State { ... },
        "event": { "type": "<event.type>", "<PROP>": "<event.PROP>" }
      },
      ...
    ]
  },
  ...
}

示例

¥Example

import { createMachine } from 'xstate';
import { getShortestPaths } from '@xstate/graph';

const feedbackMachine = createMachine({
  id: 'feedback',
  initial: 'question',
  states: {
    question: {
      on: {
        CLICK_GOOD: 'thanks',
        CLICK_BAD: 'form',
        CLOSE: 'closed',
        ESC: 'closed'
      }
    },
    form: {
      on: {
        SUBMIT: 'thanks',
        CLOSE: 'closed',
        ESC: 'closed'
      }
    },
    thanks: {
      on: {
        CLOSE: 'closed',
        ESC: 'closed'
      }
    },
    closed: {
      type: 'final'
    }
  }
});

const shortestPaths = getShortestPaths(feedbackMachine);

console.log(shortestPaths);
// => {
//   '"question"': {
//     state: State { value: 'question', context: undefined },
//     weight: 0,
//     path: []
//   },
//   '"thanks"': {
//     state: State { value: 'thanks', context: undefined },
//     weight: 1,
//     path: [
//       {
//         state: State { value: 'question', context: undefined },
//         event: { type: 'CLICK_GOOD' }
//       }
//     ]
//   },
//   '"form"': {
//     state: State { value: 'form', context: undefined },
//     weight: 1,
//     path: [
//       {
//         state: State { value: 'question', context: undefined },
//         event: { type: 'CLICK_BAD' }
//       }
//     ]
//   },
//   '"closed"': {
//     state: State { value: 'closed', context: undefined },
//     weight: 1,
//     path: [
//       {
//         state: State { value: 'question', context: undefined },
//         event: { type: 'CLOSE' }
//       }
//     ]
//   }
// };

# getSimplePaths(machine, options?)

参数

¥Arguments

返回 machine (opens new window)简单的路径 (opens new window) 作为映射对象,其中:

¥Returns the simple paths (opens new window) of a machine (opens new window) as a mapped object, where the:

  • key 是字符串化状态

    ¥key is the stringified state

  • value 是一个具有以下属性的对象:

    ¥value is an object with the properties:

paths 中的每个 path 都是一个段数组,其中路径的每个段都是一个具有以下属性的对象:

¥Each path in paths is an array of segments, where each segment of the path is an object with the properties:

  • state - 该段的 State (opens new window)

    ¥state - the State (opens new window) of the segment

  • event - 将 machine 从路径中的状态转换到下一个状态的事件对象

    ¥event - the event object that transitions the machine from the state to the next state in the path

每条路径都从初始状态开始。

¥Every path starts with the initial state.

整体对象结构如下所示:

¥The overall object structure looks like this:

{
  "<SERIALIZED STATE>": {
    "state": State { ... },
    "paths": [
      [
        {
          "state": State { ... },
          "event": { "type": "<event.type>", "<PROP>": "<event.PROP>" }
        },
        {
          "state": State { ... },
          "event": { "type": "<event.type>", "<PROP>": "<event.PROP>" }
        },
        ...
      ],
      ...
    ]
  },
  ...
}

示例

¥Example

import { createMachine } from 'xstate';
import { getSimplePaths } from '@xstate/graph';

const feedbackMachine = createMachine({
  id: 'feedback',
  initial: 'question',
  states: {
    question: {
      on: {
        CLICK_GOOD: 'thanks',
        CLICK_BAD: 'form',
        CLOSE: 'closed',
        ESC: 'closed'
      }
    },
    form: {
      on: {
        SUBMIT: 'thanks',
        CLOSE: 'closed',
        ESC: 'closed'
      }
    },
    thanks: {
      on: {
        CLOSE: 'closed',
        ESC: 'closed'
      }
    },
    closed: {
      type: 'final'
    }
  }
});

const simplePaths = getSimplePaths(feedbackMachine);

console.log(simplePaths);
// => {
//   '"question"': {
//     state: { value: 'question', context: undefined },
//     paths: [[]]
//   },
//   '"thanks"': {
//     state: { value: 'thanks', context: undefined },
//     paths: [
//       [
//         {
//           state: { value: 'question', context: undefined },
//           event: { type: 'CLICK_GOOD' }
//         }
//       ],
//       [
//         {
//           state: { value: 'question', context: undefined },
//           event: { type: 'CLICK_BAD' }
//         },
//         {
//           state: { value: 'form', context: undefined },
//           event: { type: 'SUBMIT' }
//         }
//       ]
//     ]
//   },
//   '"closed"': {
//     state: { value: 'closed', context: undefined },
//     paths: [
//       [
//         {
//           state: { value: 'question', context: undefined },
//           event: { type: 'CLICK_GOOD' }
//         },
//         {
//           state: { value: 'thanks', context: undefined },
//           event: { type: 'CLOSE' }
//         }
//       ],
//       [
//         {
//           state: { value: 'question', context: undefined },
//           event: { type: 'CLICK_GOOD' }
//         },
//         {
//           state: { value: 'thanks', context: undefined },
//           event: { type: 'ESC' }
//         }
//       ],
//       ...
//     ]
//   },
//   ...
// };

# getPathFromEvents(machine, events)

参数

¥Arguments

返回具有以下键的路径对象:

¥Returns a path object with the following keys:

import { createMachine } from 'xstate';
import { getSimplePaths } from '@xstate/graph';

const feedbackMachine = createMachine({
  id: 'feedback',
  initial: 'question',
  states: {
    question: {
      on: {
        CLICK_GOOD: 'thanks',
        CLICK_BAD: 'form',
        CLOSE: 'closed',
        ESC: 'closed'
      }
    },
    form: {
      on: {
        SUBMIT: 'thanks',
        CLOSE: 'closed',
        ESC: 'closed'
      }
    },
    thanks: {
      on: {
        CLOSE: 'closed',
        ESC: 'closed'
      }
    },
    closed: {
      type: 'final'
    }
  }
});

const path = getPathFromEvents(feedbackMachine, [
  { type: 'CLICK_GOOD' },
  { type: 'SUBMIT' },
  { type: 'CLOSE' }
]);

console.log(path);
// => {
//   state: { value: 'closed' },
//   segments: [
//     {
//       state: { value: 'question' },
//       event: { type: 'CLICK_GOOD' },
//     },
//     {
//       state: { value: 'form' },
//       event: { type: 'SUBMIT' },
//     },
//     {
//       state: { value: 'thanks' },
//       event: { type: 'CLOSE' },
//     },
//   ],
// }

# toDirectedGraph(machine)

machine 转换为有向图结构。

¥Converts a machine to a directed graph structure.

争论 类型 描述
machine createMachine(...) 创建的 XState 机器 转换为有向图结构的机器

示例

¥Example

import { toDirectedGraph } from '@xstate/graph';

const machine = createMachine({/* ... */});

const digraph = toDirectedGraph(machine);

// returns an object with this structure:
{
  id: '...',
  stateNode: /* StateNode */,
  children: [
    { id: '...', children: [/* ... */], edges: [/* ... */] },
    { id: '...', /* ... */ },
    // ...
  ],
  edges: [
    { source: /* ... */, target: /* ... */, transition: /* ... */ }
    // ...
  ]
}

# 选项

¥Options

可以将选项传递到 getShortestPathsgetSimplePaths 中以自定义如何遍历机器表示的图:

¥Options can be passed into getShortestPaths or getSimplePaths to customize how the graph represented by the machine should be traversed:

  • events - 事件类型到用于这些事件的事件对象数组的映射

    ¥events - a mapping of event types to an array of event objects to be used for those events

  • filter - 确定是否应遍历 state 的函数。如果是 false,遍历算法将假定状态为 "seen" 并忽略遍历它。

    ¥filter - a function that determines whether a state should be traversed. If false, the traversal algorithm(s) will assume the state was "seen" and ignore traversing it.

示例

¥Examples

在下面的示例中,INC 事件扩展为包括两个可能的事件,其中 value: 1value: 2 作为负载。它还确保 state.context.count <= 5;否则,这台机器将被无限地遍历。

¥In the below example, the INC event is expanded to include two possible events, with value: 1 and value: 2 as the payload. It also ensures that the state.context.count <= 5; otherwise, this machine would be traversed infinitely.

const counterMachine = createMachine({
  id: 'counter',
  initial: 'active',
  context: { count: 0 },
  states: {
    active: {
      on: {
        INC: {
          actions: assign({ count: (ctx, e) => ctx.count + e.value })
        }
      }
    }
  }
});

const shortestPaths = getShortestPaths(counterMachine, {
  events: {
    INC: [
      { type: 'INC', value: 1 },
      { type: 'INC', value: 2 }
    ]
  },
  filter: (state) => state.context.count <= 5
});

console.log(shortestPaths);
// => {
//   '"active" | {"count":0}': {
//     state: { value: 'active', context: { count: 0 } },
//     weight: 0,
//     path: []
//   },
//   '"active" | {"count":1}': {
//     state: { value: 'active', context: { count: 1 } },
//     weight: 1,
//     path: [
//       {
//         state: { value: 'active', context: { count: 0 } },
//         event: { type: 'INC', value: 1 }
//       }
//     ]
//   },
//   '"active" | {"count":2}': {
//     state: { value: 'active', context: { count: 2 } },
//     weight: 1,
//     path: [
//       {
//         state: { value: 'active', context: { count: 0 } },
//         event: { type: 'INC', value: 2 }
//       }
//     ]
//   },
//   '"active" | {"count":3}': {
//     state: { value: 'active', context: { count: 3 } },
//     weight: 2,
//     path: [
//       {
//         state: { value: 'active', context: { count: 0 } },
//         event: { type: 'INC', value: 1 }
//       },
//       {
//         state: { value: 'active', context: { count: 1 } },
//         event: { type: 'INC', value: 2 }
//       }
//     ]
//   },
//   ...
// };