Open3

共同編集のためのライブラリ Yjs が面白そう

kobayangkobayang

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 を使っている特性上実装が厳しい?

kobayangkobayang

Yjs を使って、簡単なTODOアプリを作ってみた。
https://github.com/kobayang/yjs-todo-app

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 の追加が他のブラウザに伝搬されている。

yjs-todo-demo