Security News
The Risks of Misguided Research in Supply Chain Security
Snyk's use of malicious npm packages for research raises ethical concerns, highlighting risks in public deployment, data exfiltration, and unauthorized testing.
mobx-state-tree
Advanced tools
Opinionated, transactional, MobX powered state container
Opinionated state container for MobX powered applications
NPM:
npm install mobx-state-tree --save-dev
CDN:
https://unpkg.com/mobx-state-tree/mobx-state-tree.umd.js
mobx-state-tree
is a state container that combines the simplicity and ease of mutable data with the traceability of immutable data and the reactiveness and performance of observable data.
It is an opt-in state container that can be used in MobX, but also Redux based applications.
TODO: slides / reactive conf talk
Unlike MobX itself, mobx-state-tree is quite opionated on how you structure your data. This makes it possible to solve many problems generically and out of the box, like:
mobx-state-tree
tries to take the best features from both object oriented (discoverability, co-location and encapsulation), and immutable based state management approaches (transactionality, sharing functionality through composition).
mobx-state-tree
supports JSON patches, replayable actions, listeners for patches, actions and snapshots. References, maps, arrays. Just read on :)Models are at the heart of mobx-state-tree
. They simply store your data.
mobx
concept of computed
values.Example:
import {createFactory, action, arrayOf, refenceTo} from "mobx-state-tree"
const Box = createFactory({
// props
name: "",
x: 0,
y: 0,
// computed prop
get width() {
return this.name.length * 15
},
// action
move: action(function(dx, dy) {
this.x += dx
this.y += dy
})
})
const BoxStore = createFactory({
boxes: mapOf(Box),
selection: referenceTo("boxes/name"),
createBox: action(function(name) {
this.boxes.set(name, Box({ name, x: 100, y: 100}))
})
})
const boxStore = BoxStore()
boxStore.addBox("test")
boxStore.boxes.get("test").move(7, 3)
Useful methods:
createFactory(exampleModel)
: creates a new factoryclone(model)
: constructs a deep clone of the given model instanceA snapshot is a representation of a model. Snapshots are immutable and use structural sharing (sinces model can contain models, snapshots can contain other snapshots). This means that any mutation of a model results in a new snapshot (using structural sharing) of the entire state tree. This enables compatibility with any library that is based on immutable state trees.
boxStore.boxes.set("test", Box({ name: "test" }))
and boxStore.boxes.set("test", { name: "test" })
are both valid.Useful methods:
getSnapshot(model)
: returns a snapshot representing the current state of the modelonSnapshot(model, callback)
: creates a listener that fires whenever a new snapshot is available (but only one per MobX transaction).applySnapshot(model, snapshot)
: updates the state of the model and all its descendants to the state represented by the snapshotActions modify models. Actions are replayable and are therefor constrained in several ways:
A serialized action call looks like:
{
name: "setAge"
path: "/user",
args: [17]
}
Useful methods:
action(fn)
constructsonAction(model, middleware)
listens to any action that is invoked on the model or any of it's descendants. See onAction
for more details.applyAction(model, action)
invokes an action on the model according to the given action descriptionModifying a model does not only result in a new snapshot, but also in a stream of JSON-patches describing which modifications are made. Patches have the following signature:
export interface IJsonPatch {
op: "replace" | "add" | "remove"
path: string
value?: any
}
path
attribute of a patch considers the relative path of the event from the place where the event listener is attachedUseful methods:
onPatch(model, listener)
attaches a patch listener to the provided model, which will be invoked whenever the model or any of it's descendants is mutatedapplyPatch(model, patch)
applies a patch to the provided modelThe actual signature of all factory functions is (snapshot, environment) => model
.
This makes it possible to associate an environment with a factory created object.
The environment is intended to be an inmutable object context information about the environment, for example which data fetch library should be used etc.
This makes it easy to mock these kind of dependencies, as alternative to requireing singletons that might be needed inside actions.
It is recommended to only provide an environment to the root of your state tree; environments of non-roots might be lost when using functions like applySnapshot
, applyPatch
or applyAction
.
Useful methods:
getEnvironment(model, key)
Returns a value from the environment. Environments are stacked; the resolve the environment value the tree is walked up, until a model provides an environment value for the specified key.
Example:
const Store = createFactory({
users: [],
requestData: action(function() {
const fetchImpl = getEnvironment(this, "fetch")
fetchImpl("http://localhost/users").then(this.receiveData)
}),
receiveData: action(function(users) {
// etc...
})
})
const myStore = Store({ users: []}, { fetch: window.fetch })
Should all state of my app be stored in mobx-state-tree
?
No, or, not necessarily. An application can use both state trees and vanilla MobX observables at the same time.
State trees are primarily designed to store your domain data, as this kind of state is often distributed and not very local.
For, for example, local component state, vanilla MobX observables might often be simpler to use.
Some model constructions which are supported by mobx are not supported by mobx-state-tree
mobx-state-tree
does currently not support inheritance / subtyping. This could be changed by popular demand, but not supporting inheritance avoids the need to serialize type information or keeping a (global) type registerySo far this might look a lot like an immutable state tree as found for example in Redux apps, but there are a few differences:
import { resolve } from "mobx-state-tree"
class Message {
@observable _author = "103"
@computed get author() {
return resolve(this, `/users`, this._author)
}
set author(author: User) {
this._author = author ? author.id : null
}
}
FAQs
Opinionated, transactional, MobX powered state container
The npm package mobx-state-tree receives a total of 70,255 weekly downloads. As such, mobx-state-tree popularity was classified as popular.
We found that mobx-state-tree demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 8 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Snyk's use of malicious npm packages for research raises ethical concerns, highlighting risks in public deployment, data exfiltration, and unauthorized testing.
Research
Security News
Socket researchers found several malicious npm packages typosquatting Chalk and Chokidar, targeting Node.js developers with kill switches and data theft.
Security News
pnpm 10 blocks lifecycle scripts by default to improve security, addressing supply chain attack risks but sparking debate over compatibility and workflow changes.