Skip to content

Commit

Permalink
feat: pass the node to postProcess snapshot transformers (#2116)
Browse files Browse the repository at this point in the history
When transforming the snapshot for a node, it would be quite handy to get access to the node the snapshot was generated for, so you can traverse the tree to get at data you want to put in the snapshot. Stored properties of that node are already given to you in the hook in the snapshot, but computeds, data from parents, tree environment data, or volatiles aren't accessible for use in snapshot transformers unless the node is passed in.

Woop woop!
  • Loading branch information
airhorns authored Nov 17, 2023
1 parent 0694a96 commit 4e1ee91
Show file tree
Hide file tree
Showing 5 changed files with 179 additions and 63 deletions.
55 changes: 48 additions & 7 deletions __tests__/core/snapshotProcessor.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { observable } from "mobx"
import {
types,
getSnapshot,
Expand All @@ -6,7 +7,10 @@ import {
detach,
clone,
SnapshotIn,
getNodeId
getNodeId,
Instance,
getType,
onSnapshot
} from "../../src"

describe("snapshotProcessor", () => {
Expand Down Expand Up @@ -51,26 +55,58 @@ describe("snapshotProcessor", () => {
})

test("post processor", () => {
let model: Instance<typeof Model>
const Model = types.model({
m: types.snapshotProcessor(M, {
postProcessor(sn): { x: number } {
postProcessor(sn, node): { x: number; val?: string } {
expect(node).toBeTruthy()

return {
...sn,
x: Number(sn.x)
x: Number(sn.x),
val: node.x
}
}
})
})
const model = Model.create({
model = Model.create({
m: { x: "5" }
})
unprotect(model)
expect(model.m.x).toBe("5")
expect(getSnapshot(model).m.x).toBe(5)
expect(getSnapshot(model).m.val).toBe("5")
// reconciliation
model.m = cast({ x: "6" })
expect(model.m.x).toBe("6")
expect(getSnapshot(model).m.x).toBe(6)
expect(getSnapshot(model).m.val).toBe("6")
})

test("post processor that observes other observables recomputes when they change", () => {
let model: Instance<typeof Model>
const atom = observable.box("foo")

const Model = types.model({
m: types.snapshotProcessor(M, {
postProcessor(sn, node): { x: number; val: string } {
return {
...sn,
x: Number(sn.x),
val: atom.get()
}
}
})
})
model = Model.create({
m: { x: "5" }
})
const newSnapshot = jest.fn()
onSnapshot(model, newSnapshot)
expect(getSnapshot(model).m.val).toBe("foo")
atom.set("bar")
expect(getSnapshot(model).m.val).toBe("bar")
expect(newSnapshot).toHaveBeenCalledTimes(1)
})

test("pre and post processor", () => {
Expand Down Expand Up @@ -143,7 +179,8 @@ describe("snapshotProcessor", () => {
test("post processor", () => {
const Model = types.model({
m: types.snapshotProcessor(M, {
postProcessor(sn): number {
postProcessor(sn, node): number {
expect(node).toMatch(/5|6/)
return Number(sn)
}
})
Expand Down Expand Up @@ -224,7 +261,9 @@ describe("snapshotProcessor", () => {
test("post processor", () => {
const Model = types.model({
m: types.snapshotProcessor(M, {
postProcessor(sn): number[] {
postProcessor(sn, node): number[] {
expect(node).toBeDefined()
expect(node.length).toEqual(1)
return sn.map((n) => Number(n))
}
})
Expand Down Expand Up @@ -308,7 +347,9 @@ describe("snapshotProcessor", () => {
test("post processor", () => {
const Model = types.model({
m: types.snapshotProcessor(M, {
postProcessor(sn): { x: number } {
postProcessor(sn, node): { x: number } {
expect(node.size).toBe(1)

return {
...sn,
x: Number(sn.x)
Expand Down
2 changes: 2 additions & 0 deletions docs/concepts/snapshots.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,5 @@ Useful methods:
- `getSnapshot(model, applyPostProcess)`: returns a snapshot representing the current state of the model
- `onSnapshot(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 snapshot

`mobx-state-tree` also supports customizing snapshots when they are generated or when they are applied with [`types.snapshotProcessor`](/overview/hooks).
Loading

0 comments on commit 4e1ee91

Please sign in to comment.