Plain Text Input Component with Y.Text

I’m not aware of anything, but I thought it was an interesting question so I put together a minimal demo. It has one additional dependency on fast-diff.

Demo: View in CodeSandbox

import * as Y from "yjs";
import { useState } from "react";
import diff from "fast-diff";

const doc = new Y.Doc();
const ytext = doc.getText();

/** A hook to read and set a YText value. */
function useText(ytext) {
  const [text, setText] = useState(ytext.toString());
  ytext.observe(() => {
    setText(ytext.toString());
  });
  const setYText = (textNew) => {
    const delta = diffToDelta(diff(text, textNew));
    ytext.applyDelta(delta);
  };
  return [text, setYText];
}

/** Convert a fast-diff result to a YJS delta. */
function diffToDelta(diffResult) {
  return diffResult.map(([op, value]) =>
    op === diff.INSERT
      ? { insert: value }
      : op === diff.EQUAL
      ? { retain: value.length }
      : op === diff.DELETE
      ? { delete: value.length }
      : null
  );
}

export default function App() {
  const [text, setText] = useText(ytext);
  return (
    <div>
      <input
        onInput={(e) => {
          setText(e.target.value);
        }}
      />
      <p>Text: {text}</p>
    </div>
  );
}