🦁
Yjs における UndoManager と origin の関係
Yjs における UndoManager と origin の関係
Y.UndoManager は、特定の origin に対する変更のみを Undo/Redo の対象として記録する。
この挙動を理解しておかないと、意図した変更が Undo できない場合がある。
origin の指定と trackedOrigins
Y.Doc.transact() の第2引数には、任意の origin を指定できる。
doc.transact(() => {
yItem.set("completed", true);
}, origin);
一方で、Y.UndoManager には trackedOrigins オプションがあり、指定された origin の変更のみが記録対象となる。
const undoManager = new Y.UndoManager(itemMap, {
trackedOrigins: new Set([origin])
});
このとき、origin の参照が === によって一致しない場合、UndoManager はその変更を無視する。
origin が一致しないケース
以下のようなコードは UndoManager に記録されない。
doc.transact(() => {
yItem.set("completed", true);
}, Symbol("client")); // 新規生成されるため参照が一致しない
Symbol("client") のように毎回新しく生成される値を origin に使用すると、trackedOrigins に一致する参照が存在しないため、Undo の対象とならない。
origin を省略した場合
transact() の第2引数を省略した場合、origin は undefined として扱われる。
doc.transact(() => {
...
});
この場合、UndoManager において trackedOrigins に undefined が含まれていなければ、Undo 対象とはならない。Undo を有効にしたい場合は、以下のように設定する必要がある。
new Y.UndoManager(itemMap, {
// trackedOriginsを設定しない
});
推奨パターン
明示的に origin を扱う場合は、参照が一致するように以下のように定義して使い回す。
// shared.ts
export const clientOrigin = Symbol("client");
// UndoManager
new Y.UndoManager(itemMap, {
trackedOrigins: new Set([clientOrigin])
});
// transact
doc.transact(() => {
...
}, clientOrigin);
備考
-
originは複数の UndoManager による変更区別や、ローカル/リモート操作の識別に用いることができる -
originにSymbolを用いることで、衝突のない一意な識別が可能となる
Discussion