Skip to content

Commit 981c020

Browse files
feat(overmind): allow functions, use derived for derived and new statemachine api
BREAKING CHANGE: new statemachine api and derived requires derived function
1 parent 34a86af commit 981c020

File tree

7 files changed

+170
-117
lines changed

7 files changed

+170
-117
lines changed

packages/node_modules/overmind/src/config/statechart.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,7 @@ export function statechart<C extends IConfiguration, Charts extends Statecharts
308308
}) as any,
309309
state: Object.assign(state, {
310310
states: getInitialStates(charts),
311-
actions: ((state) => getCanTransitionActions(actions, charts, state)) as any,
311+
actions: derived((state) => getCanTransitionActions(actions, charts, state)) as any,
312312
matches: derived((state: any) => (match) => {
313313
const matchPaths = getMatchPaths(match)
314314
const statesWithoutRootChartIndicator = state.states.map((statePath) => statePath.filter((path) => path !== CHART))

packages/node_modules/overmind/src/derived.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
import { EventType, Events } from './internalTypes'
1111

1212
export const IS_DERIVED = Symbol('IS_DERIVED')
13+
export const IS_DERIVED_CONSTRUCTOR = Symbol('IS_DERIVED_CONSTRUCTOR')
1314

1415
export class Derived {
1516
private isDirty: boolean = true

packages/node_modules/overmind/src/index.ts

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {
1313
VALUE,
1414
} from 'proxy-state-tree'
1515

16-
import { Derived, IS_DERIVED } from './derived'
16+
import { Derived, IS_DERIVED, IS_DERIVED_CONSTRUCTOR } from './derived'
1717
import { Devtools, DevtoolsMessage } from './Devtools'
1818
import {
1919
DefaultMode,
@@ -72,7 +72,10 @@ export { SERIALIZE, rehydrate } from './rehydrate'
7272

7373
export { Statemachine, statemachine } from './statemachine'
7474

75-
export const derived = <D extends (state: IState, rootState: IState) => any>(cb: D): D extends (state: IState, rootState: IState) => infer O ? O : never => cb as any
75+
export const derived = <D extends (state: IState, rootState: IState) => any>(cb: D): D extends (state: IState, rootState: IState) => infer O ? O : never => {
76+
cb[IS_DERIVED_CONSTRUCTOR] = true
77+
return cb as any
78+
}
7679

7780
/** This type can be overwriten by app developers if they want to avoid
7881
* typing and then they can import `Action`, `Operation` etc. directly from
@@ -385,11 +388,18 @@ export class Overmind<ThisConfig extends IConfiguration>
385388
return { func, value: func(eventHub, tree, proxyStateTree, path.split(this.delimiter)) }
386389
}
387390

388-
const derived = new Derived(func) as any
391+
if (func[IS_DERIVED_CONSTRUCTOR]) {
392+
const derived = new Derived(func) as any
393+
394+
return {
395+
func: derived,
396+
value: derived(eventHub, tree, proxyStateTree, path.split(this.delimiter))
397+
}
398+
}
389399

390400
return {
391-
func: derived,
392-
value: derived(eventHub, tree, proxyStateTree, path.split(this.delimiter))
401+
func,
402+
value: func
393403
}
394404
},
395405
onGetter: devmode

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

Lines changed: 112 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -7,42 +7,41 @@ describe('Statemachine', () => {
77

88
test('should set initial state', () => {
99

10-
type State = {
11-
machine: Statemachine<'FOO' | 'BAR'>
12-
}
13-
const state: State = {
14-
machine: statemachine<'FOO' | 'BAR'>({
15-
initial: 'FOO',
16-
states: {
17-
FOO: ['BAR'],
18-
BAR: ['FOO']
19-
}
20-
})
10+
type States = {
11+
state: 'FOO'
12+
} | {
13+
state: 'BAR'
2114
}
15+
16+
const state = statemachine<States>({
17+
FOO: ['BAR'],
18+
BAR: ['FOO']
19+
}, {
20+
state: 'FOO'
21+
})
2222
const config = {
2323
state,
2424
}
2525

2626
const overmind = createOvermindMock(config)
2727

28-
expect(overmind.state.machine.current).toBe('FOO')
28+
expect(overmind.state.state).toBe('FOO')
2929
})
30-
test.only('should transition state', () => {
31-
type State = {
32-
machine: Statemachine<'FOO' | 'BAR'>
33-
}
34-
35-
const state: State = {
36-
machine: statemachine<'FOO' | 'BAR'>({
37-
initial: 'FOO',
38-
states: {
39-
FOO: ['BAR'],
40-
BAR: ['FOO']
41-
}
42-
})
43-
}
30+
test('should transition state', () => {
31+
type States = {
32+
state: 'FOO'
33+
} | {
34+
state: 'BAR'
35+
}
36+
37+
const state = statemachine<States>({
38+
FOO: ['BAR'],
39+
BAR: ['FOO']
40+
}, {
41+
state: 'FOO'
42+
})
4443
const transition: Action = ({ state }) => {
45-
state.machine.BAR()
44+
state.BAR()
4645
}
4746

4847
const config = {
@@ -56,25 +55,24 @@ describe('Statemachine', () => {
5655

5756
const overmind = createOvermindMock(config)
5857
overmind.actions.transition()
59-
expect(overmind.state.machine.current).toBe('BAR')
58+
expect(overmind.state.state).toBe('BAR')
6059
})
6160
test('should ignore transition to invalid state', () => {
6261

63-
type State = {
64-
machine: Statemachine<'FOO' | 'BAR'>
62+
type States = {
63+
state: 'FOO'
64+
} | {
65+
state: 'BAR'
6566
}
6667

67-
const state: State = {
68-
machine: statemachine<'FOO' | 'BAR'>({
69-
initial: 'FOO',
70-
states: {
71-
FOO: [],
72-
BAR: ['FOO']
73-
}
74-
})
75-
}
68+
const state = statemachine<States>({
69+
FOO: [],
70+
BAR: ['FOO']
71+
}, {
72+
state: 'FOO'
73+
})
7674
const transition: Action = ({ state }) => {
77-
state.machine.BAR()
75+
state.BAR()
7876
}
7977

8078
const config = {
@@ -88,29 +86,29 @@ describe('Statemachine', () => {
8886

8987
const overmind = createOvermindMock(config)
9088
overmind.actions.transition()
91-
expect(overmind.state.machine.current).toBe('FOO')
89+
expect(overmind.state.state).toBe('FOO')
9290
})
9391
test('should run entry and exit transition', () => {
9492
expect.assertions(3)
95-
type State = {
96-
machine: Statemachine<'FOO' | 'BAR'>
93+
type States = {
94+
state: 'FOO'
95+
} | {
96+
state: 'BAR'
9797
}
9898

99-
const state: State = {
100-
machine: statemachine<'FOO' | 'BAR'>({
101-
initial: 'FOO',
102-
states: {
103-
FOO: ['BAR'],
104-
BAR: ['FOO']
105-
}
99+
const state = statemachine<States>({
100+
FOO: ['BAR'],
101+
BAR: ['FOO']
102+
}, {
103+
state: 'FOO'
106104
})
107-
}
105+
108106
const transition: Action = ({ state }) => {
109-
state.machine.BAR(() => {
110-
expect(state.machine.current).toBe('BAR')
111-
state.machine.FOO()
107+
state.BAR(() => {
108+
expect(state.state).toBe('BAR')
109+
state.FOO()
112110
}, () => {
113-
expect(state.machine.current).toBe('BAR')
111+
expect(state.state).toBe('BAR')
114112
})
115113
}
116114

@@ -125,26 +123,26 @@ describe('Statemachine', () => {
125123

126124
const overmind = createOvermindMock(config)
127125
overmind.actions.transition()
128-
expect(overmind.state.machine.current).toBe('FOO')
126+
expect(overmind.state.state).toBe('FOO')
129127
})
130128
test('should flush changes to transitions', () => {
131129
expect.assertions(1)
132130

133-
type State = {
134-
machine: Statemachine<'FOO' | 'BAR'>
131+
type States = {
132+
state: 'FOO'
133+
} | {
134+
state: 'BAR'
135135
}
136136

137-
const state: State = {
138-
machine: statemachine<'FOO' | 'BAR'>({
139-
initial: 'FOO',
140-
states: {
141-
FOO: ['BAR'],
142-
BAR: ['FOO']
143-
}
137+
const state = statemachine<States>({
138+
FOO: ['BAR'],
139+
BAR: ['FOO']
140+
}, {
141+
state: 'FOO'
144142
})
145-
}
143+
146144
const transition: Action = ({ state }) => {
147-
state.machine.BAR()
145+
state.BAR()
148146
}
149147

150148
const config = {
@@ -157,7 +155,7 @@ describe('Statemachine', () => {
157155
interface Action extends IAction<typeof config, void, void> {}
158156

159157
const overmind = createOvermind(config)
160-
overmind.reaction((state) => state.machine.current, (value) => {
158+
overmind.reaction((state) => state.state, (value) => {
161159
expect(value).toBe('BAR')
162160
})
163161
overmind.actions.transition()
@@ -166,23 +164,20 @@ describe('Statemachine', () => {
166164
test('should error when mutating async in transitions', async () => {
167165
expect.assertions(1)
168166

169-
type State = {
170-
machine: Statemachine<'FOO' | 'BAR'>
171-
foo: string
167+
type States = {
168+
state: 'FOO'
169+
} | {
170+
state: 'BAR'
172171
}
173172

174-
const state: State = {
175-
machine: statemachine<'FOO' | 'BAR'>({
176-
initial: 'FOO',
177-
states: {
178-
FOO: ['BAR'],
179-
BAR: ['FOO']
180-
}
181-
}),
182-
foo: 'bar'
183-
}
173+
const state = statemachine<States>({
174+
FOO: ['BAR'],
175+
BAR: ['FOO']
176+
}, {
177+
state: 'FOO'
178+
})
184179
const transition: Action = ({ state }) => {
185-
return state.machine.BAR(async () => {
180+
return state.BAR(async () => {
186181
await Promise.resolve()
187182
expect(state[PROXY_TREE].master.mutationTree.isBlocking).toBe(true)
188183
})
@@ -201,4 +196,39 @@ describe('Statemachine', () => {
201196

202197
return overmind.actions.transition()
203198
})
199+
200+
test('should reset a statemachine', () => {
201+
expect.assertions(1)
202+
203+
type States = {
204+
state: 'FOO'
205+
} | {
206+
state: 'BAR'
207+
}
208+
209+
const state = statemachine<States>({
210+
FOO: ['BAR'],
211+
BAR: ['FOO']
212+
}, {
213+
state: 'FOO'
214+
})
215+
216+
const transition: Action = ({ state }) => {
217+
state.BAR()
218+
}
219+
220+
const config = {
221+
state,
222+
actions: {
223+
transition
224+
}
225+
}
226+
227+
interface Action extends IAction<typeof config, void, void> {}
228+
229+
const overmind = createOvermind(config)
230+
overmind.actions.transition()
231+
overmind.state.reset()
232+
expect(overmind.state.state).toBe('FOO')
233+
})
204234
})

0 commit comments

Comments
 (0)