# @xstate/immer
这些 XState v4 文档不再维护
XState v5 现已推出!阅读有关 XState v5 的更多信息 (opens new window) 和 查看 XState v5 文档 (opens new window)。
¥XState v5 is out now! Read more about XState v5 (opens new window) and check out the XState v5 docs (opens new window).
@xstate/immer package (opens new window) 包含将 Immer (opens new window) 与 XState (opens new window) 结合使用的实用程序。
¥The @xstate/immer package (opens new window) contains utilities for using Immer (opens new window) with XState (opens new window).
# 快速开始
¥Quick Start
包含在 @xstate/immer
中:
¥Included in @xstate/immer
:
assign()
- Immer 操作允许你以方便的方式一成不变地分配给机器context
¥
assign()
- an Immer action that allows you to immutably assign to machinecontext
in a convenient waycreateUpdater()
- 一个有用的函数,允许你一致地定义上下文更新事件事件创建者并分配操作。(以下 看一个例子)¥
createUpdater()
- a useful function that allows you to cohesively define a context update event event creator and assign action, all together. (See an example below)
安装
immer
、xstate
、@xstate/immer
:¥Install
immer
,xstate
,@xstate/immer
:
npm install immer xstate @xstate/immer
注意:你不需要从 immer
中 import
任何内容;它是 @xstate/immer
的对等依赖,因此必须安装。
¥Note: You don't need to import
anything from immer
; it is a peer-dependency of @xstate/immer
, so it must be installed.
导入 Immer 实用程序:
¥Import the Immer utilities:
import { createMachine, interpret } from 'xstate';
import { assign, createUpdater } from '@xstate/immer';
const levelUpdater = createUpdater('UPDATE_LEVEL', (ctx, { input }) => {
ctx.level = input;
});
const toggleMachine = createMachine({
id: 'toggle',
context: {
count: 0,
level: 0
},
initial: 'inactive',
states: {
inactive: {
on: {
TOGGLE: {
target: 'active',
// Immutably update context the same "mutable"
// way as you would do with Immer!
actions: assign((ctx) => ctx.count++)
}
}
},
active: {
on: {
TOGGLE: {
target: 'inactive'
},
// Use the updater for more convenience:
[levelUpdater.type]: {
actions: levelUpdater.action
}
}
}
}
});
const toggleService = interpret(toggleMachine)
.onTransition((state) => {
console.log(state.context);
})
.start();
toggleService.send({ type: 'TOGGLE' });
// { count: 1, level: 0 }
toggleService.send(levelUpdater.update(9));
// { count: 1, level: 9 }
toggleService.send({ type: 'TOGGLE' });
// { count: 2, level: 9 }
toggleService.send(levelUpdater.update(-100));
// Notice how the level is not updated in 'inactive' state:
// { count: 2, level: 9 }
# API
# assign(recipe)
返回一个 XState 事件对象,该对象将更新计算机的 context
以反映 recipe
函数中对 context
所做的更改 ("mutations")。
¥Returns an XState event object that will update the machine's context
to reflect the changes ("mutations") to context
made in the recipe
function.
recipe
与传递给 Immer 的 produce(val, recipe)
函数 (opens new window) 的函数类似,此外,你还获得与传递给 assign(assigner)
的普通 XState 分配器相同的参数(context
、event
、meta
)。
¥The recipe
is similar to the function that you would pass to Immer's produce(val, recipe)
function (opens new window)), with the addition that you get the same arguments as a normal XState assigner passed to assign(assigner)
(context
, event
, meta
).
assign
的参数:
¥Arguments for assign
:
争论 | 类型 | 描述 |
---|---|---|
recipe | function | 生成 "mutations" 至 context 的函数。参见 Immer 文档 (opens new window)。 |
recipe
的参数:
¥Arguments for recipe
:
争论 | 类型 | 描述 |
---|---|---|
context | any | 当前状态的上下文数据 |
event | 事件对象 | 接收到的事件对象 |
meta | 分配元对象 | 包含元数据的对象,例如 state 、SCXML _event 等。 |
import { createMachine } from 'xstate';
import { assign } from '@xstate/immer';
const userMachine = createMachine({
id: 'user',
context: {
name: null,
address: {
city: null,
state: null,
country: null
}
},
initial: 'active',
states: {
active: {
on: {
CHANGE_COUNTRY: {
actions: assign((context, event) => {
context.address.country = event.value;
})
}
}
}
}
});
const { initialState } = userMachine;
const nextState = userMachine.transition(initialState, {
type: 'UPDATE_COUNTRY',
country: 'USA'
});
nextState.context.address.country;
// => 'USA'
# createUpdater(eventType, recipe)
返回一个对于创建 context
更新程序有用的对象。
¥Returns an object that is useful for creating context
updaters.
争论 | 类型 | 描述 |
---|---|---|
eventType | string | Immer 更新事件的事件类型 |
recipe | function | 接收 context 和 Immer 将 event 对象更新为 "mutate" 和 context 的函数 |
Immer 更新 event
对象是包含以下内容的对象:
¥An Immer update event
object is an object that contains:
type
:指定的eventType
¥
type
: theeventType
specifiedinput
:更新事件的 "payload"¥
input
: the "payload" of the update event
createUpdater(...)
返回的对象是一个更新程序对象,包含:
¥The object returned by createUpdater(...)
is an updater object containing:
type
:eventType
传递到createUpdater(eventType, ...)
。这用于指定将发生更新的转换。¥
type
: theeventType
passed intocreateUpdater(eventType, ...)
. This is used for specifying transitions in which the update will occur.action
:将更新context
的分配操作对象。¥
action
: the assign action object that will update thecontext
.update
:事件创建者接受input
并返回event
对象,其中指定的eventType
和input
将传递给recipe(context, event)
。¥
update
: the event creator that takes in theinput
and returns anevent
object with the specifiedeventType
andinput
that will be passed torecipe(context, event)
.
⚠️ 注意:.update(...)
事件的创造者是纯粹的;它只返回一个分配动作对象,并不直接更新 context
。
¥⚠️ Note: The .update(...)
event creator is pure; it only returns an assign action object, and doesn't directly update context
.
import { createMachine } from 'xstate';
import { createUpdater } from '@xstate/immer';
// The second argument is an Immer update event that looks like:
// {
// type: 'UPDATE_NAME',
// input: 'David' // or any string
// }
const nameUpdater = createUpdater('UPDATE_NAME', (context, { input }) => {
context.name = input;
});
const ageUpdater = createUpdater('UPDATE_AGE', (context, { input }) => {
context.age = input;
});
const formMachine = createMachine({
initial: 'editing',
context: {
name: '',
age: null
},
states: {
editing: {
on: {
// The updater.type can be used directly for transitions
// where the updater.action function will be applied
[nameUpdater.type]: { actions: nameUpdater.action },
[ageUpdater.type]: { actions: ageUpdater.action }
}
}
}
});
const service = interpret(formMachine)
.onTransition((state) => {
console.log(state.context);
})
.start();
// The event object sent will look like:
// {
// type: 'UPDATE_NAME',
// input: 'David'
// }
service.send(nameUpdater.update('David'));
// => { name: 'David', age: null }
// The event object sent will look like:
// {
// type: 'UPDATE_AGE',
// input: 100
// }
service.send(ageUpdater.update(100));
// => { name: 'David', age: 100 }
# TypeScript
要正确键入 Immer assign
操作创建者,请将 context
和 event
类型作为泛型类型传递:
¥To properly type the Immer assign
action creator, pass in the context
and event
types as generic types:
interface SomeContext {
name: string;
}
interface SomeEvent {
type: 'SOME_EVENT';
value: string;
}
// ...
{
actions: assign<SomeContext, SomeEvent>((context, event) => {
context.name = event.value;
// ... etc.
});
}
要正确键入 createUpdater
,请将 context
和特定 ImmerUpdateEvent<...>
(见下文)类型作为泛型类型传递:
¥To properly type createUpdater
, pass in the context
and the specific ImmerUpdateEvent<...>
(see below) types as generic types:
import { createUpdater, ImmerUpdateEvent } from '@xstate/immer';
// This is the same as:
// {
// type: 'UPDATE_NAME';
// input: string;
// }
type NameUpdateEvent = ImmerUpdateEvent<'UPDATE_NAME', string>;
const nameUpdater = createUpdater<SomeContext, NameUpdateEvent>(
'UPDATE_NAME',
(ctx, { input }) => {
ctx.name = input;
}
);
// You should use NameUpdateEvent directly as part of the event type
// in createMachine<SomeContext, SomeEvent>.
这是上一个表单示例的完整类型示例:
¥Here is a fully typed example of the previous form example:
import { createMachine } from 'xstate';
import { createUpdater, ImmerUpdateEvent } from '@xstate/immer';
interface FormContext {
name: string;
age: number | undefined;
}
type NameUpdateEvent = ImmerUpdateEvent<'UPDATE_NAME', string>;
type AgeUpdateEvent = ImmerUpdateEvent<'UPDATE_AGE', number>;
const nameUpdater = createUpdater<FormContext, NameUpdateEvent>(
'UPDATE_NAME',
(ctx, { input }) => {
ctx.name = input;
}
);
const ageUpdater = createUpdater<FormContext, AgeUpdateEvent>(
'UPDATE_AGE',
(ctx, { input }) => {
ctx.age = input;
}
);
type FormEvent =
| NameUpdateEvent
| AgeUpdateEvent
| {
type: 'SUBMIT';
};
const formMachine = createMachine({
schema: {
context: {} as FormContext,
events: {} as FormEvent
},
initial: 'editing',
context: {
name: '',
age: undefined
},
states: {
editing: {
on: {
[nameUpdater.type]: { actions: nameUpdater.action },
[ageUpdater.type]: { actions: ageUpdater.action },
SUBMIT: 'submitting'
}
},
submitting: {
// ...
}
}
});