Multiview observable state management engine based on MobX
- Observable state with concept of a living tree
- Multiple views of the same state (private parts supported)
- Runtime views configuration
- Patch (JsonPatch format) and snapshot generation for each view
- Embedded patch serializer with compression based on PatchPack
- Custom schema serialization support
- Typescript syntax support out of the box
- Works perfect with MagX server
Central in mosx is the concept of a state tree. The state tree consists of mutable objects, arrays and maps. Every object and property of state tree can be public or private. Public object's/properties can be traced by all listeners, but private are available only for listeners with access. So this means that every listener can have their own view of the same state tree. Access to private object/properties can be updated in real-time.
On each mutation of state automatically generate patch for all listeners in (JsonPatch format. Patch can be encoded via embedded serializers, or with custom serializer implementation. Snapshot of state tree is also available for every listener.
Since mosx uses MobX behind the scenes, computed properties are supported OOB. Observable properties are also supported, but hidden for listeners and can be used in computed properties.
Another core design goal of mosx is to offer a easy and clean way to create multiview state with great Typescript decorators syntax. Everything you need to make your state trackable, just wrap your class and properties with @mx decorator.
npm install --save mosx
https://udamir.github.io/mosx/
The easiest way to try out Mosx is using the magx-examples project:
git clone https://github.com/udamir/magx-examples.git
cd magx-examples
npm install
To run the MagX server, run npm start
- Define MosX object for Player:
import { Mosx, mx } from "mosx"
@mx.Object
class Player {
@mx public x = Math.floor(Math.random() * 400)
@mx public y = Math.floor(Math.random() * 400)
}
- Define MosX state:
@mx.Object
export class State {
@mx public players = new Map<string, Player>()
public createPlayer(id: string) {
const player = new Player()
this.players.set(id, player)
}
public removePlayer(id: string) {
this.players.delete(id)
}
public movePlayer(id: string, movement: any) {
const player = this.players.get(id)
if (!player) { return }
player.x += movement.x ? movement.x * 10 : 0
player.y += movement.y ? movement.y * 10 : 0
}
}
- Start track state changes:
const state = new State()
const tracker = Mosx.createTracker(state)
tracker.onPatch((change) => console.log(change))
- Update state and check track patches
state.createPlayer("Player 1")
// Patch in JsonPatch format
// {
// op: 'add',
// path: '/players/Player 1',
// value: { x: 77, y: 393 }
// }
state.movePlayer("Player 1", { x: 5, y: -5 })
// { op: 'replace', path: '/players/Player 1/x', value: 127 }
// { op: 'replace', path: '/players/Player 1/y', value: 343 }
- Get snapshot of current state
const snapshot = Mosx.getSnapshot(state)
console.log(snapshot)
// { players: { 'Player 1': { x: 127, y: 343 } } }