Architecture
Undo and Redo
Understand how the undo and redo system works.
Undo and Redo
The undo and redo functionality is implemented in the useWorkflow hook.
// src/shared/kits/workflow/hooks/use-workflow.ts
// ...
function reducer(state: State, action: Action): State {
// ...
}
function init(schema: WorkflowSchema): State {
const workflow = toWorkflow(schema);
return {
history: {
workflows: [workflow],
pointer: 0,
current: workflow,
},
center: null,
};
}
export function useWorkflow(schema: WorkflowSchema): [State, Dispatch<Action>] {
return useReducer(reducer, schema, init);
}
This hook returns an object with a history property that contains:
workflows: The workflow states used for undo/redo, including past and future states.pointer: The index inworkflowsrepresenting the current position in the history.current: The currently active workflow state.
The reducer handles both undo/redo logic and the application of workflow changes.
When applying a change, applyWorkflowChange returns a memory property that defines whether that change has to be saved in the history, as shown here.
// src/shared/kits/workflow/hooks/use-workflow.ts
// ...
function reducer(state: State, action: Action): State {
switch (action.type) {
/**
* Applies all incoming workflow changes. Each change may request the resulting
* workflow to be saved in the history (`memory`), in which case a new entry
* is added and any forward history (redo states) is discarded.
*/
case "applyWorkflowChanges": {
const workflows: Workflow[] = [...state.history.workflows];
let current: Workflow = state.history.current;
let pointer: number = state.history.pointer;
let center: string | null = null;
for (const change of action.changes) {
const output = applyWorkflowChange(change, current);
current = output.workflow;
if (output.memory) {
pointer++;
workflows.splice(pointer, workflows.length - pointer, {
...output.workflow,
widget: null,
active: null,
});
}
if (output.center) {
center = output.center;
}
}
return {
history: { workflows: workflows, pointer, current },
center: center,
};
}
// ...
}
}
// ...
This is because not all changes, such as selecting or dragging nodes, need to be saved.