| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Name | Name | Last commit date | ||
|---|---|---|---|---|
 |  | |||
 |  | |||
 |  | |||
 |  | |||
 |  | |||
 |  | |||
 |  | |||
 |  | |||
 |  | |||
The core idea is to use patches to keep the UI in sync between client and server, multiple clients, or multiple windows.
It uses Immer as an interface for state mutations and provides a convenient way to group mutations into a single transaction, and enables undo/redo out of the box.
import store, { sync } from "immerhin";
// Create containers for each state. Sync engine only cares that the result has a "get()" and a "set(newValue)"
const container1 = atom(initialValue);
const container2 = atom(initialValue);
// - Explicitely enable containers for transactions
// - Define a namespace for each container, so that server knows which object it has to patch.
store.register("container1", container1);
store.register("container2", container2);
// Creating the actual transaction that will:
// - generate patches
// - update states
// - inform all subscribers
// - register a transaction for potential undo/redo and sync calls
store.createTransaction(
[container1, container2, ...rest],
(value1, value2, ...rest) => {
mutateValue(value1);
mutateValue(value2);
// ...
}
);
// Setup periodic sync with a fetch, or do this with Websocket
setInterval(async () => {
const entries = sync();
await fetch("/patch", { method: "POST", payload: JSON.stringify(entries) });
}, 1000);
// Undo/redo
store.undo();
store.redo();A container is an interface that implements .get() and .set(value) methods so that a value can be updated and propagated to all consumers.
You can use anything to create containers, it could be a Redux store, could be an observable, a nano state or nanostores.
You can use the same container instance to subscribe to the changes across the entire application.
Example using nano state:
import { atom } from "nanostores";
import { useStore } from "@nanostores/react";
const myContainer = atom(initialValue);
// I can call a set from anywhere
myContainer.set(newValue);
// I can subscribe to updates in React
const Component = () => {
const value = useStore(myContainer);
};We register containers for two reasons:
Example
store.register("myName", myContainer);A transaction is a set of changes applied to a set of states. When you apply changes to the states inside a transaction, you are essentially telling the engine which changes are associated with the same user action so that undo/redo can use that as a single step to work with.
A call into store.createTransaction()does all of this:
Example
store.createTransaction(
[container1, container2, ...rest],
(value1, value2, ...rest) => {
mutateValue(value1);
mutateValue(value2);
// ...
}
);Calling undo() and redo() functions will essentially apply the right patch for the value and dispatch the update.
The sync() function returns you all changes queued up for a sync since the last call. With the return from sync(), you can do anything you want, for example, send it to your server.
Example
// Setup periodic sync with a fetch, or do this with Websocket
setInterval(async () => {
const entries = sync();
await fetch("/patch", { method: "POST", payload: JSON.stringify(entries) });
}, 1000);Example entries:
[
{
"transactionId": "6243062b469f516835327f65",
"changes": [
{
"namespace": "root",
"patches": [
{
"op": "replace",
"path": ["children", 1],
"value": {
"component": "Box",
"id": "6241f55791596f2467df9c2a",
"style": {},
"children": []
}
},
{
"op": "replace",
"path": ["children", 2],
"value": {
"component": "Box",
"id": "6241f55a91596f2467df9c36",
"style": {},
"children": []
}
},
{
"op": "replace",
"path": ["children", "length"],
"value": 3
}
]
}
]
}
]If you want to have multiple separate undoable states, create a separate store for each. They add to the same sync queue in the end.
import { Store } from "immerhin";
const store = new Store();| Back | FazBrowse Home | New Git URL |