Skip to content

Commit e758380

Browse files
feat(overmind): rather add disposing of statemachines
1 parent fab2557 commit e758380

File tree

2 files changed

+51
-30
lines changed

2 files changed

+51
-30
lines changed

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

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -456,25 +456,40 @@ describe('Statemachine', () => {
456456

457457
expect(() => overmind.actions.transition()).not.toThrow()
458458
})
459-
test('should deal with transition effects', async () => {
459+
test('should dispose nested machines', async () => {
460460
type States = {
461461
current: 'FOO'
462462
} | {
463463
current: 'BAR',
464-
number: number
465464
}
466465

467-
const state = statemachine<States>({
466+
type ParentStates = {
467+
current: 'FOO'
468+
nested: typeof nested
469+
} | {
470+
current: 'BAR'
471+
}
472+
473+
const nested = statemachine<States>({
474+
FOO: ['BAR'],
475+
BAR: ['FOO']
476+
}, {
477+
current: 'FOO'
478+
})
479+
480+
const state = statemachine<ParentStates>({
468481
FOO: ['BAR'],
469482
BAR: ['FOO']
470483
}, {
471484
current: 'FOO',
485+
nested
472486
})
473487
const transition: Action = ({ state }) => {
474-
return state.transition('BAR', { number: 123 }, () => {
475-
return Promise.resolve(4321)
476-
}, (barState, value) => {
477-
barState.number = value
488+
state.matches('FOO', (fooState) => {
489+
const ref = fooState.nested
490+
state.transition('BAR', {})
491+
expect(fooState.nested).toBe(undefined)
492+
expect(ref.current).toBe('FOO')
478493
})
479494
}
480495

@@ -485,13 +500,12 @@ describe('Statemachine', () => {
485500
}
486501
}
487502

488-
interface Action extends IAction<typeof config, void, Promise<void>> {}
503+
interface Action extends IAction<typeof config, void, void> {}
489504

490505
const overmind = createOvermindMock(config)
491506
// @ts-ignore
492507
overmind.isStrict = true
493508

494-
await overmind.actions.transition()
495-
expect(overmind.state.current === 'BAR' && overmind.state.number === 4321).toBe(true)
509+
overmind.actions.transition()
496510
})
497511
})

packages/node_modules/overmind/src/statemachine.ts

Lines changed: 27 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { PROXY_TREE, VALUE } from 'proxy-state-tree'
1+
import { PROXY_TREE, VALUE, PATH } from 'proxy-state-tree'
22

33
import { deepCopy } from './utils'
44
import { IState } from '.'
@@ -34,18 +34,15 @@ export interface MachineMethods<States extends TStates, Base extends IState = {}
3434
matches<T extends States["current"], O = void>(state: T | T[], cb: (current: Statemachine<Base, States, States extends {
3535
current: T
3636
} ? States : never>) => O): O;
37-
transition<T extends States["current"], O = void, O2 = O>(
37+
transition<T extends States["current"], O = void>(
3838
state: T,
3939
newState: States extends {
4040
current: T
4141
} ? Exact<Omit<States, 'current'>, {}> extends never ? Omit<States, 'current'> & Partial<Base> : Exact<Partial<Base>, {}> extends never ? Partial<Base> : { [NO_PROP]?: true} : never,
4242
effectsCallback?: ((current: Statemachine<Base, States, States extends {
4343
current: T
44-
} ? States : never>) => O),
45-
stateCallback?: ((current: Statemachine<Base, States, States extends {
46-
current: T
47-
} ? States : never>, value: O extends Promise<infer V> ? V : O) => O2)
48-
): O extends Promise<any> ? Promise<O2> : O2;
44+
} ? States : never>) => O)
45+
): O;
4946
}
5047

5148
export type Statemachine<Base extends IState, States extends TStates, State extends TStates = States> = Base extends never ? State & MachineMethods<States> : Base & State & MachineMethods<States, Base>
@@ -56,15 +53,20 @@ const BASE = Symbol('BASE')
5653
const STATE = Symbol('STATE')
5754
const CURRENT_KEYS = Symbol('CURRENT_KEYS')
5855
const NO_PROP = Symbol('NO_PROP')
56+
const IS_DISPOSED = Symbol('IS_DISPOSED')
5957

6058
export class StateMachine<Base extends IState, States extends TStates, State extends TStates = States, > {
6159
current: State["current"]
6260
private [INITIAL_STATE]: State["current"]
6361
private [STATE]: any
6462
private [BASE]: any
63+
private [IS_DISPOSED] = false
6564
private clone() {
6665
return new StateMachine(this[TRANSITIONS], deepCopy(this[STATE]), deepCopy(this[BASE]))
6766
}
67+
private dispose() {
68+
this
69+
}
6870
constructor(transitions: StatemachineTransitions<States>, state: States, base: Base) {
6971
this[STATE] = state
7072
this[INITIAL_STATE] = state.current
@@ -74,6 +76,13 @@ export class StateMachine<Base extends IState, States extends TStates, State ext
7476
Object.assign(this, state, base)
7577
}
7678
matches(states, cb) {
79+
if (this[VALUE][IS_DISPOSED]) {
80+
if (process.env.NODE_ENV === 'development') {
81+
console.warn(`Overmind - The statemachine at "${this[PATH]}" has been disposed, but you tried to match on it`)
82+
}
83+
return
84+
}
85+
7786
states = Array.isArray(states) ? states : [states]
7887

7988
if (states.includes(this.current)) {
@@ -89,7 +98,14 @@ export class StateMachine<Base extends IState, States extends TStates, State ext
8998
return result
9099
}
91100
}
92-
transition(state, newState, effectsCallback, stateCallback) {
101+
transition(state, newState, effectsCallback) {
102+
if (this[VALUE][IS_DISPOSED]) {
103+
if (process.env.NODE_ENV === 'development') {
104+
console.warn(`Overmind - The statemachine at "${this[PATH]}" has been disposed, but you tried to transition on it`)
105+
}
106+
return
107+
}
108+
93109
const transitions = this[VALUE][TRANSITIONS]
94110

95111
if (transitions[this.current].includes(state)) {
@@ -100,6 +116,9 @@ export class StateMachine<Base extends IState, States extends TStates, State ext
100116

101117
this[VALUE][CURRENT_KEYS].forEach((key) => {
102118
if (!baseKeys.includes(key)) {
119+
if (this[key] instanceof StateMachine) {
120+
this[key].dispose()
121+
}
103122
delete this[key]
104123
}
105124
})
@@ -115,18 +134,6 @@ export class StateMachine<Base extends IState, States extends TStates, State ext
115134
result = effectsCallback(this)
116135
}
117136

118-
if (stateCallback && result instanceof Promise) {
119-
return result.then((value) => {
120-
if (this.current !== state) {
121-
return value
122-
}
123-
tree.enableMutations()
124-
const result = stateCallback(this, value)
125-
tree.blockMutations()
126-
return result
127-
})
128-
}
129-
130137
return result
131138
} else if (process.env.NODE_ENV === 'development' && state !== this.current) {
132139
console.warn(`Overmind Statemachine - You tried to transition into "${state}", but it is not a valid transition. The valid transitions are ${JSON.stringify(transitions[this.current])}`)

0 commit comments

Comments
 (0)