Skip to content

Commit

Permalink
feat(overmind): rather add disposing of statemachines
Browse files Browse the repository at this point in the history
  • Loading branch information
christianalfoni committed Jan 23, 2021
1 parent fab2557 commit e758380
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 30 deletions.
34 changes: 24 additions & 10 deletions packages/node_modules/overmind/src/statemachine.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -456,25 +456,40 @@ describe('Statemachine', () => {

expect(() => overmind.actions.transition()).not.toThrow()
})
test('should deal with transition effects', async () => {
test('should dispose nested machines', async () => {
type States = {
current: 'FOO'
} | {
current: 'BAR',
number: number
}

const state = statemachine<States>({
type ParentStates = {
current: 'FOO'
nested: typeof nested
} | {
current: 'BAR'
}

const nested = statemachine<States>({
FOO: ['BAR'],
BAR: ['FOO']
}, {
current: 'FOO'
})

const state = statemachine<ParentStates>({
FOO: ['BAR'],
BAR: ['FOO']
}, {
current: 'FOO',
nested
})
const transition: Action = ({ state }) => {
return state.transition('BAR', { number: 123 }, () => {
return Promise.resolve(4321)
}, (barState, value) => {
barState.number = value
state.matches('FOO', (fooState) => {
const ref = fooState.nested
state.transition('BAR', {})
expect(fooState.nested).toBe(undefined)
expect(ref.current).toBe('FOO')
})
}

Expand All @@ -485,13 +500,12 @@ describe('Statemachine', () => {
}
}

interface Action extends IAction<typeof config, void, Promise<void>> {}
interface Action extends IAction<typeof config, void, void> {}

const overmind = createOvermindMock(config)
// @ts-ignore
overmind.isStrict = true

await overmind.actions.transition()
expect(overmind.state.current === 'BAR' && overmind.state.number === 4321).toBe(true)
overmind.actions.transition()
})
})
47 changes: 27 additions & 20 deletions packages/node_modules/overmind/src/statemachine.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { PROXY_TREE, VALUE } from 'proxy-state-tree'
import { PROXY_TREE, VALUE, PATH } from 'proxy-state-tree'

import { deepCopy } from './utils'
import { IState } from '.'
Expand Down Expand Up @@ -34,18 +34,15 @@ export interface MachineMethods<States extends TStates, Base extends IState = {}
matches<T extends States["current"], O = void>(state: T | T[], cb: (current: Statemachine<Base, States, States extends {
current: T
} ? States : never>) => O): O;
transition<T extends States["current"], O = void, O2 = O>(
transition<T extends States["current"], O = void>(
state: T,
newState: States extends {
current: T
} ? Exact<Omit<States, 'current'>, {}> extends never ? Omit<States, 'current'> & Partial<Base> : Exact<Partial<Base>, {}> extends never ? Partial<Base> : { [NO_PROP]?: true} : never,
effectsCallback?: ((current: Statemachine<Base, States, States extends {
current: T
} ? States : never>) => O),
stateCallback?: ((current: Statemachine<Base, States, States extends {
current: T
} ? States : never>, value: O extends Promise<infer V> ? V : O) => O2)
): O extends Promise<any> ? Promise<O2> : O2;
} ? States : never>) => O)
): O;
}

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

export class StateMachine<Base extends IState, States extends TStates, State extends TStates = States, > {
current: State["current"]
private [INITIAL_STATE]: State["current"]
private [STATE]: any
private [BASE]: any
private [IS_DISPOSED] = false
private clone() {
return new StateMachine(this[TRANSITIONS], deepCopy(this[STATE]), deepCopy(this[BASE]))
}
private dispose() {
this
}
constructor(transitions: StatemachineTransitions<States>, state: States, base: Base) {
this[STATE] = state
this[INITIAL_STATE] = state.current
Expand All @@ -74,6 +76,13 @@ export class StateMachine<Base extends IState, States extends TStates, State ext
Object.assign(this, state, base)
}
matches(states, cb) {
if (this[VALUE][IS_DISPOSED]) {
if (process.env.NODE_ENV === 'development') {
console.warn(`Overmind - The statemachine at "${this[PATH]}" has been disposed, but you tried to match on it`)
}
return
}

states = Array.isArray(states) ? states : [states]

if (states.includes(this.current)) {
Expand All @@ -89,7 +98,14 @@ export class StateMachine<Base extends IState, States extends TStates, State ext
return result
}
}
transition(state, newState, effectsCallback, stateCallback) {
transition(state, newState, effectsCallback) {
if (this[VALUE][IS_DISPOSED]) {
if (process.env.NODE_ENV === 'development') {
console.warn(`Overmind - The statemachine at "${this[PATH]}" has been disposed, but you tried to transition on it`)
}
return
}

const transitions = this[VALUE][TRANSITIONS]

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

this[VALUE][CURRENT_KEYS].forEach((key) => {
if (!baseKeys.includes(key)) {
if (this[key] instanceof StateMachine) {
this[key].dispose()
}
delete this[key]
}
})
Expand All @@ -115,18 +134,6 @@ export class StateMachine<Base extends IState, States extends TStates, State ext
result = effectsCallback(this)
}

if (stateCallback && result instanceof Promise) {
return result.then((value) => {
if (this.current !== state) {
return value
}
tree.enableMutations()
const result = stateCallback(this, value)
tree.blockMutations()
return result
})
}

return result
} else if (process.env.NODE_ENV === 'development' && state !== this.current) {
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])}`)
Expand Down

0 comments on commit e758380

Please sign in to comment.