Open3
共同編集のためのライブラリ Yjs が面白そう
Yjs とは
Yjs: https://github.com/yjs/yjs
Documantation: https://docs.yjs.dev/
Yjs は CRDT の実装。CRDT は a conflict-free replicated data type の略で、分散コンピューティングに特化したデータ構造のこと。
Yjs は、Shared types と呼ばれる、Map や Array などの型を持っている。この型の変更が自動的に他の peer に伝えられてコンフリクトを起こさずマージできる。
どこで使われているか
主要なテキストエディタの編集にはYjsのbindingが実装されていそう。最近話題になっている、tiptapの共同編集の内部で Yjs が使われているのを見て、自分は興味を持った。
Draft.js の binding は残念ながらない。Draft.js は Immutable.js を使っている特性上実装が厳しい?
Yjs を使って、簡単なTODOアプリを作ってみた。
TODOアプリのロジックは以下のような感じ。
import * as Y from "yjs";
import { WebsocketProvider } from "y-websocket";
import { useCallback, useEffect, useState } from "react";
const yDoc = new Y.Doc();
// Sync clients with the y-websocket provider
const wsProvider = new WebsocketProvider(
  "ws://localhost:3001",
  "todo-demo",
  yDoc
);
wsProvider.on("status", (event: { status: any }) => {
  console.log(event.status); // logs "connected" or "disconnected"
});
const yTodoArray = yDoc.getArray<string>("todo-demo:todo-list");
function useYArrayValue<T extends unknown>(yArray: Y.Array<T>) {
  const [value, setValue] = useState<T[]>(yArray.toArray());
  useEffect(() => {
    yArray.observe((_) => {
      setValue(yArray.toArray());
    });
  }, [yArray]);
  return value;
}
export const useYTodo = () => {
  const [currentTodo, setCurrentTodo] = useState("");
  const todos = useYArrayValue<string>(yTodoArray);
  const addTodo = useCallback(
    (todo: string) => {
      if (todos.includes(todo)) {
        console.error(`This todo has beed stacked!`);
        return;
      }
      yTodoArray.push([todo]);
    },
    [todos]
  );
  return { currentTodo, setCurrentTodo, addTodo, todos } as const;
};
やってることは以下。
- 
y-websocketで起動したサーバーとソケット通信を行う - 
YDocからYArrayを取得する - 
YArray.observeで、変更を検知して反映する- 
useYArrayValueがその処理に該当 
 - 
 
確かに、ちゃんと TODO の追加が他のブラウザに伝搬されている。
アルゴリズムはどうなってる?
CRDT は変更を可換な操作にだけ限定し、その操作列をデータ構造に保持することで、 Peer 同士でデータマージを Conflict-free に行うことができる理論のこと。
Yjs はさらに CRDT の最適化を行っているらしい。