# @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 machinecontextin 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: theeventTypespecifiedinput:更新事件的 "payload"¥
input: the "payload" of the update event
createUpdater(...) 返回的对象是一个更新程序对象,包含:
¥The object returned by createUpdater(...) is an updater object containing:
type:eventType传递到createUpdater(eventType, ...)。这用于指定将发生更新的转换。¥
type: theeventTypepassed 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 theinputand returns aneventobject with the specifiedeventTypeandinputthat 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: {
// ...
}
}
});