Skip to content

Commit 4e1ee91

Browse files
authored
feat: pass the node to postProcess snapshot transformers (#2116)
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!
1 parent 0694a96 commit 4e1ee91

File tree

5 files changed

+179
-63
lines changed

5 files changed

+179
-63
lines changed

__tests__/core/snapshotProcessor.test.ts

+48-7
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { observable } from "mobx"
12
import {
23
types,
34
getSnapshot,
@@ -6,7 +7,10 @@ import {
67
detach,
78
clone,
89
SnapshotIn,
9-
getNodeId
10+
getNodeId,
11+
Instance,
12+
getType,
13+
onSnapshot
1014
} from "../../src"
1115

1216
describe("snapshotProcessor", () => {
@@ -51,26 +55,58 @@ describe("snapshotProcessor", () => {
5155
})
5256

5357
test("post processor", () => {
58+
let model: Instance<typeof Model>
5459
const Model = types.model({
5560
m: types.snapshotProcessor(M, {
56-
postProcessor(sn): { x: number } {
61+
postProcessor(sn, node): { x: number; val?: string } {
62+
expect(node).toBeTruthy()
63+
5764
return {
5865
...sn,
59-
x: Number(sn.x)
66+
x: Number(sn.x),
67+
val: node.x
6068
}
6169
}
6270
})
6371
})
64-
const model = Model.create({
72+
model = Model.create({
6573
m: { x: "5" }
6674
})
6775
unprotect(model)
6876
expect(model.m.x).toBe("5")
6977
expect(getSnapshot(model).m.x).toBe(5)
78+
expect(getSnapshot(model).m.val).toBe("5")
7079
// reconciliation
7180
model.m = cast({ x: "6" })
7281
expect(model.m.x).toBe("6")
7382
expect(getSnapshot(model).m.x).toBe(6)
83+
expect(getSnapshot(model).m.val).toBe("6")
84+
})
85+
86+
test("post processor that observes other observables recomputes when they change", () => {
87+
let model: Instance<typeof Model>
88+
const atom = observable.box("foo")
89+
90+
const Model = types.model({
91+
m: types.snapshotProcessor(M, {
92+
postProcessor(sn, node): { x: number; val: string } {
93+
return {
94+
...sn,
95+
x: Number(sn.x),
96+
val: atom.get()
97+
}
98+
}
99+
})
100+
})
101+
model = Model.create({
102+
m: { x: "5" }
103+
})
104+
const newSnapshot = jest.fn()
105+
onSnapshot(model, newSnapshot)
106+
expect(getSnapshot(model).m.val).toBe("foo")
107+
atom.set("bar")
108+
expect(getSnapshot(model).m.val).toBe("bar")
109+
expect(newSnapshot).toHaveBeenCalledTimes(1)
74110
})
75111

76112
test("pre and post processor", () => {
@@ -143,7 +179,8 @@ describe("snapshotProcessor", () => {
143179
test("post processor", () => {
144180
const Model = types.model({
145181
m: types.snapshotProcessor(M, {
146-
postProcessor(sn): number {
182+
postProcessor(sn, node): number {
183+
expect(node).toMatch(/5|6/)
147184
return Number(sn)
148185
}
149186
})
@@ -224,7 +261,9 @@ describe("snapshotProcessor", () => {
224261
test("post processor", () => {
225262
const Model = types.model({
226263
m: types.snapshotProcessor(M, {
227-
postProcessor(sn): number[] {
264+
postProcessor(sn, node): number[] {
265+
expect(node).toBeDefined()
266+
expect(node.length).toEqual(1)
228267
return sn.map((n) => Number(n))
229268
}
230269
})
@@ -308,7 +347,9 @@ describe("snapshotProcessor", () => {
308347
test("post processor", () => {
309348
const Model = types.model({
310349
m: types.snapshotProcessor(M, {
311-
postProcessor(sn): { x: number } {
350+
postProcessor(sn, node): { x: number } {
351+
expect(node.size).toBe(1)
352+
312353
return {
313354
...sn,
314355
x: Number(sn.x)

docs/concepts/snapshots.md

+2
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,5 @@ Useful methods:
5656
- `getSnapshot(model, applyPostProcess)`: returns a snapshot representing the current state of the model
5757
- `onSnapshot(model, callback)`: creates a listener that fires whenever a new snapshot is available (but only one per MobX transaction).
5858
- `applySnapshot(model, snapshot)`: updates the state of the model and all its descendants to the state represented by the snapshot
59+
60+
`mobx-state-tree` also supports customizing snapshots when they are generated or when they are applied with [`types.snapshotProcessor`](/overview/hooks).

0 commit comments

Comments
 (0)