This project provides utilities for synchronizing Yjs documents with plain objects, applying diffs/deltas, and handling nested structures, arrays, and text types. It aims to simplify the integration of Yjs with React applications by offering a straightforward and vanilla approach.
- Introduction
- Features
- Installation
- Usage
- Running Tests
- API Reference
- Examples
- License
- Acknowledgments
vyjs (Vanilla Yjs) is a utility library designed to simplify the integration of Yjs with React and other JavaScript frameworks. It provides a minimalistic approach to synchronize Yjs documents with plain JavaScript objects, making it easier to build real-time collaborative applications without the overhead of complex abstractions.
- Vanilla Integration: Offers a straightforward way to use Yjs without additional abstractions.
- React Compatibility: Simplifies the integration of Yjs with React applications.
- Synchronization: Automatically synchronize Yjs documents with plain JavaScript objects.
- Difference Application: Apply the difference between two JSON objects to Yjs types.
- Nested Structures: Support for nested maps, arrays, and text types within Yjs documents.
- Conflict Resolution: Handle concurrent changes with Yjs's built-in conflict resolution mechanisms.
- Comprehensive Tests: A suite of tests demonstrating various synchronization scenarios.
Install vyjs via npm:
npm install vyjs
Import the necessary modules:
import * as Y from 'yjs';
import { getYjsEventsHandler } from 'vyjs';
const ydoc = new Y.Doc();
const yMap = ydoc.getMap('rootMap');
let currentValue = {};
const callback = (updatedValue) => {
currentValue = updatedValue;
console.log('Updated Value:', currentValue);
};
const eventsHandler = getYjsEventsHandler(currentValue, callback);
yMap.observeDeep(eventsHandler);
// Now, any changes made to yMap will be reflected in currentValue via the callback:
ydoc.transact(() => {
yMap.set('number', 42);
yMap.set('text', new Y.Text('Hello, Yjs!'));
});
vyjs makes it easier to integrate Yjs with React by keeping your component state in sync with Yjs documents.
import React, { useEffect, useState } from 'react';
import * as Y from 'yjs';
import { getYjsEventsHandler } from 'vyjs';
const ydoc = new Y.Doc();
const yMap = ydoc.getMap('rootMap');
function App() {
const [state, setState] = useState({});
useEffect(() => {
let currentValue = {};
const callback = (updatedValue) => {
setState(updatedValue);
};
const eventsHandler = getYjsEventsHandler(currentValue, callback);
yMap.observeDeep(eventsHandler);
return () => {
yMap.unobserveDeep(eventsHandler);
};
}, []);
return (
<div>
<h1>Yjs and React Integration</h1>
<pre>{JSON.stringify(state, null, 2)}</pre>
</div>
);
}
export default App;
With this setup, any changes to the Yjs document will automatically update the React component's state, ensuring seamless synchronization between the collaborative data and your UI.
Import the applyJsonDiffToYjs
function:
import { applyJsonDiffToYjs } from 'vyjs';
import * as Y from 'yjs';
const ydoc = new Y.Doc();
const yMap = ydoc.getMap('map');
const oldValue = {
number: 42,
text: 'Hello, Yjs!',
};
const newValue = {
number: 100,
text: 'Hi',
status: true,
};
// Initialize yMap with oldValue
Object.keys(oldValue).forEach((key) => {
yMap.set(key, oldValue[key]);
});
// Apply the difference between oldValue and newValue to yMap
applyJsonDiffToYjs(oldValue, newValue, yMap);
console.log(yMap.get('number')); // Output: 100
console.log(yMap.get('status')); // Output: true
console.log(yMap.get('text').toString()); // Output: 'Hi'
npm test
Returns an event handler function that synchronizes changes from a Yjs document to a plain JavaScript object.
- Parameters:
currentValue
(Object): The current value of the plain JavaScript object.callback
(Function): The function to call with the updated value.
- Returns:
- (Function): An event handler to be used with
yMap.observeDeep()
.
- (Function): An event handler to be used with
Usage:
const eventsHandler = getYjsEventsHandler(currentValue, callback);
yMap.observeDeep(eventsHandler);
Applies the difference between two JSON objects to a Yjs type.
- Parameters:
oldValue
(any): The original JSON value.newValue
(any): The new JSON value.yValue
(Y.Map | Y.Array | Y.Text): The Yjs type to apply the changes to.yValueParent
(Y.Map | Y.Array | undefined): The parent of the Yjs type.yValueKey
(string | number | undefined): The key or index in the parent whereyValue
is located.
- Returns:
void
Usage:
applyJsonDiffToYjs(oldValue, newValue, yValue, yValueParent, yValueKey);
Converts a JSON object to a Yjs type.
- Parameters:
object
(any): The JSON object to convert.
- Returns:
- Y.Map | Y.Array | Y.Text | primitive value
Usage:
const yType = jsonToYType(jsonObject);
Synchronize nested maps and arrays between a Yjs document and a plain object.
ydoc.transact(() => {
const nestedMap = new Y.Map();
nestedMap.set('key1', 'value1');
yMap.set('nested', nestedMap);
});
console.log(currentValue);
// Output:
// {
// nested: {
// key1: 'value1',
// },
// }
// Update nested map
ydoc.transact(() => {
const nestedMap = yMap.get('nested');
nestedMap.set('key2', 'value2');
});
console.log(currentValue);
// Output:
// {
// nested: {
// key1: 'value1',
// key2: 'value2',
// },
// }
This project is licensed under the MIT License. See the LICENSE file for details.
- Yjs: A powerful CRDT implementation for building collaborative applications.