Mutative

柯里化

创建稍后接收基础状态及额外参数的 producer,或创建稍后完成最终处理的草稿。

除了 create(base, recipe),create() 还支持两种柯里化形式:只传更新函数会得到 producer(生产者函数),稍后再传入基础状态;只传基础状态则得到可稍后完成最终处理的草稿。

Producer(生产者函数)

create(recipe, options?) 返回 producer (base, ...args) => nextState。它为第一个参数创建草稿,再将草稿和其余参数传给更新函数:

import { create } from 'mutative';

type Todo = { text: string; done: boolean };

const toggle = create((draft: Todo[], index: number) => {
  draft[index].done = !draft[index].done;
});

const todos: Todo[] = [
  { text: 'learn', done: false },
  { text: 'ship', done: false },
];

const nextTodos = toggle(todos, 1);

expect(nextTodos[1].done).toBe(true);
expect(nextTodos[0]).toBe(todos[0]);

接收 action 参数的 producer 就是 reducer,可用于 Redux、useReducer() 等接收 reducer 的库:

import { create } from 'mutative';

type State = { todos: { text: string; done: boolean }[] };

type Action =
  | { type: 'add'; text: string }
  | { type: 'toggle'; index: number };

const reducer = create((draft: State, action: Action) => {
  switch (action.type) {
    case 'add':
      draft.todos.push({ text: action.text, done: false });
      break;
    case 'toggle':
      draft.todos[action.index].done = !draft.todos[action.index].done;
      break;
  }
});

const initialState: State = { todos: [] };
const state = [
  { type: 'add', text: 'learn' } as const,
  { type: 'toggle', index: 0 } as const,
].reduce(reducer, initialState);

expect(state.todos).toEqual([{ text: 'learn', done: true }]);
  • 调用时的选项作用于 producer 生成的每个状态。例如 create(recipe, { enablePatches: true }) 返回的 producer 会生成 [nextState, patches, inversePatches]。
  • producer 以自身的 this 调用更新函数,因此可以作为对象方法使用。
  • 异步更新函数对应的 producer 返回下一个状态的 Promise。
  • TypeScript 中如上例标注草稿参数类型,即可为 producer 的参数与结果提供类型。

延后完成最终处理的草稿

create(base, options?) 返回基础状态的草稿和 finalize() 函数,适合无法放进单个更新函数的修改,例如分散在多个函数中的更新。finalize() 的返回值与传入更新函数的 create() 一致:

import { create } from 'mutative';

const baseState = {
  foobar: { foo: 'str', bar: 'str' },
  baz: { text: 'str' },
};

const [draft, finalize] = create(baseState, { enablePatches: true });

draft.foobar.bar = 'baz';

const [state, patches, inversePatches] = finalize();

expect(state).toEqual({
  foobar: { foo: 'str', bar: 'baz' },
  baz: { text: 'str' },
});
expect(state.baz).toBe(baseState.baz);
expect(patches).toEqual([
  { op: 'replace', path: ['foobar', 'bar'], value: 'baz' },
]);
expect(inversePatches).toEqual([
  { op: 'replace', path: ['foobar', 'bar'], value: 'str' },
]);

此形式要求基础状态可创建草稿,因为没有更新函数可以通过返回值替换状态。

finalize() 只能调用一次。如果它抛错(例如开发构建拒绝某个补丁路径),草稿不会像更新函数失败时那样被撤销,而是仍可写入。再次调用 finalize() 可能返回包含已撤销草稿的状态,补丁也可能遗漏修改。此时应从基础状态重新创建草稿。

makeCreator() 返回的 create() 支持相同形式,并使用配置的默认选项。

在 GitHub 上编辑

最后更新于 作者:unadlib

本页内容