Skip to content

Commit 36d47e2

Browse files
feat(overmind): allow rehydrate of state object as well
1 parent 0333b67 commit 36d47e2

File tree

5 files changed

+113
-15
lines changed

5 files changed

+113
-15
lines changed

packages/node_modules/overmind/src/index.test.ts

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { EventType, IAction, Overmind, createOvermindMock } from './'
1+
import { EventType, IAction, Overmind, createOvermindMock, rehydrate } from './'
22
import { namespaced } from './config'
33

44
function toJSON(obj) {
@@ -40,13 +40,17 @@ function createDefaultOvermind() {
4040
const { form, key, value } = payload
4141
form[key] = value
4242
}
43+
const rehydrateAction: Action<any> = ({ state }, newState) => {
44+
rehydrate(state, newState)
45+
}
4346
const actions = {
4447
asyncChangeFoo,
4548
changeFormValue,
4649
changeFoo,
4750
changeFooWithEffect,
4851
changeValue,
4952
waitAndChangeFoo,
53+
rehydrateAction
5054
}
5155
const effects = {
5256
hello() {
@@ -357,6 +361,28 @@ describe('Overmind', () => {
357361
).not.toThrow()
358362
expect(app.state.item.isAwesome).toBe(false)
359363
})
364+
test('should allow rehydration', () => {
365+
expect.assertions(2)
366+
const app = createDefaultOvermind()
367+
const mutation = {
368+
method: 'set',
369+
path: 'foo',
370+
args: ['bar2']
371+
}
372+
app.actions.rehydrateAction([mutation])
373+
expect(app.state.foo).toBe('bar2')
374+
app.actions.rehydrateAction({
375+
item: {
376+
isAwesome: false
377+
}
378+
})
379+
expect(app.state).toEqual({
380+
foo: 'bar2',
381+
item: {
382+
isAwesome: false
383+
}
384+
})
385+
})
360386
})
361387

362388
describe('Overmind mock', () => {

packages/node_modules/overmind/src/index.ts

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ import {
6161
makeStringifySafeMutations,
6262
mergeState,
6363
processState,
64+
rehydrateState
6465
} from './utils'
6566

6667
export * from './types'
@@ -93,20 +94,25 @@ export interface Reaction extends IReaction<Config> {}
9394
export const json = (obj: any) =>
9495
deepCopy(obj && obj[IS_PROXY] ? obj[VALUE] : obj)
9596

96-
export const rehydrate = (state: object, mutations: IMutation[]) => {
97-
mutations.forEach((mutation) => {
98-
const pathArray = mutation.path.split('.')
99-
const key = pathArray.pop()
100-
const target = pathArray.reduce((aggr, key) => aggr[key], state)
101-
102-
if (mutation.method === 'set') {
103-
target[key] = mutation.args[0]
104-
} else if (mutation.method === 'unset') {
105-
delete target[key]
106-
} else {
107-
target[key][mutation.method](...mutation.args)
108-
}
109-
})
97+
export const rehydrate = (state: IState, source: IMutation[] | IState) => {
98+
if (Array.isArray(source)) {
99+
const mutations = source as IMutation[]
100+
mutations.forEach((mutation) => {
101+
const pathArray = mutation.path.split('.')
102+
const key = pathArray.pop() as string
103+
const target = pathArray.reduce((aggr, key) => aggr[key], state as any)
104+
105+
if (mutation.method === 'set') {
106+
target[key] = mutation.args[0]
107+
} else if (mutation.method === 'unset') {
108+
delete target[key]
109+
} else {
110+
target[key][mutation.method](...mutation.args)
111+
}
112+
})
113+
} else {
114+
rehydrateState(state, source)
115+
}
110116
}
111117

112118
export interface OvermindSSR<Config extends IConfiguration>

packages/node_modules/overmind/src/utils.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { IMutation } from 'proxy-state-tree'
33

44
import { Derived } from './derived'
55
import { safeValues } from './Devtools'
6+
import { IState } from './types'
67

78
export const IS_TEST = process.env.NODE_ENV === 'test'
89
export const IS_OPERATOR = Symbol('operator')
@@ -162,3 +163,19 @@ export function createActionsProxy(actions, cb) {
162163
},
163164
})
164165
}
166+
167+
export function rehydrateState(target: IState, source: IState) {
168+
if (!target || !source) {
169+
throw new Error(`You have to pass a "target" and "source" object to rehydrate`)
170+
}
171+
172+
Object.keys(source).forEach((key) => {
173+
const value = source[key]
174+
if (typeof value === 'object' && !Array.isArray(value) && value !== null) {
175+
if (!target[key]) target[key] = {}
176+
rehydrateState(target[key] as IState, source[key] as IState)
177+
} else {
178+
target[key] = source[key]
179+
}
180+
})
181+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# rehydrate
2+
3+
It is possible to update the complete state of Overmind using the **rehydrate** tool. It allows you to update the state either with a state object or an array of mutations, typically collected from server side rendering.
4+
5+
```marksy
6+
h(Example, { name: "api/rehydrate" })
7+
```
8+
9+
The function takes into acount the state structure of Overmind which is based on objects where functions are derived state. That means it will leave derived state alone, resulting in them rather updating if any of their dependecies where updated by rehydration.
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
export default (ts) =>
2+
ts
3+
? [
4+
{
5+
fileName: 'overmind/onInitialize.ts',
6+
code: `
7+
import { OnInitialize, rehydrate } from 'overmind'
8+
9+
export const onInitialize: OnInitialize = ({ state, effects }) => {
10+
// Grab mutations from a server rendered version
11+
const mutations = window.__OVERMIND_MUTATIONS
12+
13+
rehydrate(state, mutations)
14+
15+
// Grab a previous copy of the state, for example stored in
16+
// localstorage
17+
rehydrate(state, effects.storage.get('previousState') || {})
18+
}
19+
`,
20+
},
21+
]
22+
: [
23+
{
24+
fileName: 'overmind/onInitialize.js',
25+
code: `
26+
import { rehydrate } from 'overmind'
27+
28+
export const onInitialize = ({ state, effects }) => {
29+
// Grab mutations from a server rendered version
30+
const mutations = window.__OVERMIND_MUTATIONS
31+
32+
rehydrate(state, mutations)
33+
34+
// Grab a previous copy of the state, for example stored in
35+
// localstorage
36+
rehydrate(state, effects.storage.get('previousState') || {})
37+
}
38+
`,
39+
},
40+
]

0 commit comments

Comments
 (0)