Skip to content

Instantly share code, notes, and snippets.

@HaNdTriX
Last active July 14, 2022 00:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save HaNdTriX/8c66315773ee93963c178d90f9547c15 to your computer and use it in GitHub Desktop.
Save HaNdTriX/8c66315773ee93963c178d90f9547c15 to your computer and use it in GitHub Desktop.
Simple shared conflict free peer to peer state via valtio and y.js.
import * as Y from "yjs";
import { bindProxyAndYMap } from "valtio-yjs";
import { WebrtcProvider } from "y-webrtc";
import { IndexeddbPersistence } from "y-indexeddb";
import { proxy } from "valtio";
import { useMemo, useEffect } from "react";
type WebrtcProviderOptions = ConstructorParameters<typeof WebrtcProvider>[2];
export default function useRoom<T extends Record<string, unknown>>(
roomname: string,
password: string | null,
initialState: T
) {
// eslint-disable-next-line react-hooks/exhaustive-deps
const yDoc = useMemo(() => new Y.Doc(), [roomname, password]);
// eslint-disable-next-line react-hooks/exhaustive-deps
const room = useMemo(() => proxy(initialState), [roomname, password]);
// Connect: Webrtc
useEffect(() => {
const options: Partial<WebrtcProviderOptions> = {
password,
};
const provider = new WebrtcProvider(
roomname,
yDoc,
options as WebrtcProviderOptions
);
return () => {
provider.destroy();
};
}, [yDoc, roomname, password]);
// Connect: IndexDB
useEffect(() => {
const provider = new IndexeddbPersistence(roomname, yDoc);
return () => {
provider.destroy();
};
}, [yDoc, roomname]);
// Sync to ymap
useEffect(() => {
const ymap = yDoc.getMap("map");
bindProxyAndYMap(room, ymap);
}, [room, yDoc]);
return room;
}
@HaNdTriX
Copy link
Author

HaNdTriX commented Jul 14, 2022

Warning

⚠️ DONT USE IN PRODUCTION!

Deps

npm i yjs y-indexeddb y-webrtc valtio-yjs

Usage

type Message = {
  username: string;
  createdAt: string;
  body: string;
};

type Room = {
  messages: Message[];
};

 function SomeComponent() {
  const room = useRoom<Room>(roomname, password, {
    messages: [],
  });
  const snap = useSnapshot(room);

  return (
    <>
      <div>
        {snap.messages.map((message, index) => (
          <div key={index}>{message}</div>
        ))}
      </div>
      <button
        onClick={() => {
          room.messages.push({
            username: "John",
            createdAt: new Date().toISOString(),
            body: "hello",
          });
        }}
      >
        Say Hello
      </button>
    </>
  );
}

The shape of your State is up to you :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment