Skip to content

Commit 09906e7

Browse files
committed
docs(reamde): update
1 parent 4815c7f commit 09906e7

File tree

1 file changed

+142
-1
lines changed

1 file changed

+142
-1
lines changed

README.md

Lines changed: 142 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,40 @@ A library for building Yjs collaborative web applications with Mutative.
1616
- 🚀 **Performance**: Efficient patch-based updates with structural sharing
1717
- 🔌 **Flexible**: Customizable patch application for advanced use cases
1818
- 📡 **Reactive**: Built-in subscription system for state changes
19+
-**Explicit Transactions**: Updates to Y.js are batched in transactions, you control the boundary
20+
- 🪶 **Lightweight**: Simple, small codebase with no magic or vendor lock-in
21+
- 🎨 **Non-intrusive**: Always opt-in by nature (snapshots are just plain objects)
22+
23+
## Why mutative-yjs?
24+
25+
**Do:**
26+
27+
```typescript
28+
// any operation supported by mutative
29+
binder.update((state) => {
30+
state.nested[0].key = {
31+
id: 123,
32+
p1: 'a',
33+
p2: ['a', 'b', 'c'],
34+
};
35+
});
36+
```
37+
38+
**Instead of:**
39+
40+
```typescript
41+
Y.transact(state.doc, () => {
42+
const val = new Y.Map();
43+
val.set('id', 123);
44+
val.set('p1', 'a');
45+
46+
const arr = new Y.Array();
47+
arr.push(['a', 'b', 'c']);
48+
val.set('p2', arr);
49+
50+
state.get('nested').get(0).set('key', val);
51+
});
52+
```
1953

2054
## Installation
2155

@@ -159,6 +193,28 @@ binder.unbind();
159193

160194
## Advanced Usage
161195

196+
### Structural Sharing
197+
198+
Like Mutative, `mutative-yjs` provides efficient structural sharing. Unchanged parts of the state maintain the same reference, which is especially beneficial for React re-renders:
199+
200+
```typescript
201+
const snapshot1 = binder.get();
202+
203+
binder.update((state) => {
204+
state.todos[0].done = true;
205+
});
206+
207+
const snapshot2 = binder.get();
208+
209+
// changed properties have new references
210+
snapshot1.todos !== snapshot2.todos;
211+
snapshot1.todos[0] !== snapshot2.todos[0];
212+
213+
// unchanged properties keep the same reference
214+
snapshot1.todos[1] === snapshot2.todos[1];
215+
snapshot1.todos[2] === snapshot2.todos[2];
216+
```
217+
162218
### Custom Patch Application
163219

164220
You can customize how Mutative patches are applied to Yjs data structures:
@@ -197,6 +253,8 @@ const binder = bind<MyDataType>(yMap, {
197253
});
198254
```
199255

256+
Refer to [Mutative patches documentation](https://mutative.js.org/docs/patches) for more details about patches options.
257+
200258
### Working with Y.Array
201259

202260
The library works with both `Y.Map` and `Y.Array`:
@@ -252,6 +310,85 @@ function handleUserAction() {
252310
}
253311
```
254312

313+
### Integration with React
314+
315+
Use `useSyncExternalStoreWithSelector` for optimal React integration with selective subscriptions:
316+
317+
```tsx
318+
import { bind } from 'mutative-yjs';
319+
import { useSyncExternalStoreWithSelector } from 'use-sync-external-store/with-selector';
320+
import * as Y from 'yjs';
321+
322+
// define state shape
323+
interface State {
324+
todos: Array<{ id: string; text: string; done: boolean }>;
325+
user: { name: string; email: string };
326+
}
327+
328+
const doc = new Y.Doc();
329+
330+
// define store
331+
const binder = bind<State>(doc.getMap('data'));
332+
333+
// define a helper hook
334+
function useMutativeYjs<Selection>(selector: (state: State) => Selection) {
335+
const selection = useSyncExternalStoreWithSelector(
336+
binder.subscribe,
337+
binder.get,
338+
binder.get,
339+
selector
340+
);
341+
342+
return [selection, binder.update] as const;
343+
}
344+
345+
// optionally set initial data
346+
binder.update((state) => {
347+
state.todos = [];
348+
state.user = { name: 'Guest', email: '' };
349+
});
350+
351+
// use in component
352+
function TodoList() {
353+
const [todos, update] = useMutativeYjs((s) => s.todos);
354+
355+
const addTodo = (text: string) => {
356+
update((state) => {
357+
state.todos.push({
358+
id: Math.random().toString(),
359+
text,
360+
done: false,
361+
});
362+
});
363+
};
364+
365+
const toggleTodo = (id: string) => {
366+
update((state) => {
367+
const todo = state.todos.find((t) => t.id === id);
368+
if (todo) todo.done = !todo.done;
369+
});
370+
};
371+
372+
// will only rerender when 'todos' array changes
373+
return (
374+
<div>
375+
{todos.map((todo) => (
376+
<div key={todo.id} onClick={() => toggleTodo(todo.id)}>
377+
{todo.text} {todo.done ? '' : ''}
378+
</div>
379+
))}
380+
</div>
381+
);
382+
}
383+
384+
// when done
385+
binder.unbind();
386+
```
387+
388+
### Integration with Other Frameworks
389+
390+
Contributions welcome! Please submit sample code via PR for Vue, Svelte, Angular, or other frameworks.
391+
255392
## Utility Functions
256393

257394
### `applyJsonArray(dest, source)`
@@ -367,6 +504,10 @@ This library bridges two powerful tools:
367504
- **Yjs** for CRDT-based conflict-free collaborative editing
368505
- **Mutative** for ergonomic and performant immutable state updates
369506

507+
## Credits
508+
509+
`immer-yjs` is inspired by `https://github.com/sep2/immer-yjs`.
510+
370511
## License
371512

372-
MIT © [unadlib](https://github.com/unadlib)
513+
`mutative-yjs` is [MIT licensed](https://github.com/mutativejs/mutative-yjs/blob/main/LICENSE).

0 commit comments

Comments
 (0)