共享的状态管理模式
Undo
Dojo store 使用 patch operation 跟踪底层 store 的变化。这样,Dojo 就很容易创建一组 operation,然后撤销这组 operation,以恢复一组 command 所修改的任何数据。undoOperations
是 ProcessResult
的一部分,可在 after
中间件中使用。
当一个 process 包含了多个修改 store 状态的 command,并且其中一个 command 执行失败,需要回滚时,撤销(Undo) operation 非常有用。
const undoOnFailure = () => {
return {
after: () => (error, result) {
if (error) {
result.store.apply(result.undoOperations);
}
}
};
};
const process = createProcess('do-something', [
command1, command2, command3
在执行时,任何 command 出错,则 undoOnFailure
中间件就负责应用 undoOperations
。
需要注意的是,undoOperations
仅适用于在 process 中完全执行的 command。在回滚状态时,它将不包含以下任何 operation,这些状态的变更可能是异步执行的其他 process 引起的,或者在中间件中执行的状态变更,或者直接在 store 上操作的。这些用例不在 undo 系统的范围内。
乐观更新可用于构建响应式 UI,尽管交互可能需要一些时间才能响应,例如往远程保存资源。
在成功的场景中,使用服务器响应中提供的 id
来更新已添加的 Todo
项,并将 Todo
项的颜色改为绿色,以指示已保存成功。
在出错的场景中,可以显示一个通知,说明请求失败,并将 Todo
项的颜色改为红色,同时显示一个“重试”按钮。甚至可以恢复或撤销添加的 Todo 项,以及在 process 中发生的其他任何操作。
const handleAddTodoErrorProcess = createProcess('error', [ () => [ add(path('failed'), true) ]; ]);
const addTodoErrorMiddleware = () => {
return {
if (error) {
result.store.apply(result.undoOperations);
result.executor(handleAddTodoErrorProcess);
}
}
};
};
const addTodoProcess = createProcess('add-todo', [
addTodoCommand,
calculateCountsCommand,
postTodoCommand,
calculateCountsCommand
],
[ addTodoCallback ]);
addTodoCommand
- 在应用程序状态中添加一个 todo 项postTodoCommand
- 将 todo 项提交给远程服务,并使用 process 的after
中间件在发生错误时执行进一步更改- 失败时 将恢复更改,并将 failed 状态字段设置为 true
- 成功时 使用从远程服务返回的值更新 todo 项的 id 字段
calculateCountsCommand
-postTodoCommand
成功后再运行一次
在某些情况下,在继续执行 process 之前,最好等后端调用完成。例如,当 process 从屏幕中删除一个元素时,或者 outlet 发生变化要显示不同的视图,恢复触发这些操作的状态可能会让人感到很诡异(译注:数据先从界面上删掉了,因为后台删除失败,过一会数据又出现在界面上)。
因为 process 支持异步 command,只需简单的返回 Promise
以等待结果。
并发 command
Process
支持并发执行多个 command,只需将这些 command 放在一个数组中即可。
createProcess('my-process', [commandLeft, [concurrentCommandOne, concurrentCommandTwo], commandRight]);
本示例中,commandLeft
先执行,然后并发执行 concurrentCommandOne
和 concurrentCommandTwo
。当所有的并发 command 执行完成后,就按需应用返回的结果。如果任一并发 command 出错,则不会应用任何操作。最后,执行 commandRight
。
const store = new Store({ state: myStateImpl });
任何 State
实现都必须提供四个方法,以在状态上正确的应用操作。
get<S>(path: Path<M, S>): S
接收一个Path
对象,并返回当前状态中该 path 指向的值at<S extends Path<M, Array<any>>>(path: S, index: number): Path<M, S['value'][0]>
返回一个Path
对象,该对象指向 path 定位到的数组中索引为index
的值path: StatePaths<M>
以类型安全的方式,为状态中给定的 path 生成一个Path
对象apply(operations: PatchOperation<T>[]): PatchOperation<T>[]
将提供的 operation 应用到当前状态上
Dojo Store 通过 Immutable 为 MutableState 接口提供了一个实现。如果对 store 的状态做频繁的、较深层级的更新,则这个实现可能会提高性能。在最终决定使用这个实现之前,应先测试和验证性能。
本地存储
Dojo Store 提供了一组工具来使用本地存储(local storage)。
本地存储中间件监视指定路径上的变化,并使用 collector
中提供的 id
和 path 中定义的结构,将它们存储在本地磁盘上。
使用本地存储中间件:
export const myProcess = createProcess(
'my-process',
[command],
collector('my-process', (path) => {
return [path('state', 'to', 'save'), path('other', 'state', 'to', 'save')];
})
);
来自 LocalStorage
中的 load
函数用于与 store 结合
import { load } from '@dojo/framework/stores/middleware/localStorage';
import { Store } from '@dojo/framework/stores/Store';
const store = new Store();
注意,数据要能够被序列化以便存储,并在每次调用 process 后都会覆盖数据。此实现不适用于不能序列化的数据(如 和 ArrayBuffer
)。