Skip to content

Instantly share code, notes, and snippets.

@Slooowpoke
Last active May 18, 2024 20:31
Show Gist options
  • Save Slooowpoke/92121b3f438cce15a71e48ca8c9367b0 to your computer and use it in GitHub Desktop.
Save Slooowpoke/92121b3f438cce15a71e48ca8c9367b0 to your computer and use it in GitHub Desktop.
MST + Xstate
/* eslint-disable no-param-reassign */
import {
getMembers, types
} from 'mobx-state-tree'
import {
createMachine
} from 'xstate'
import { interpret } from 'xstate/lib/interpreter'
// Pieced together from:
// https://github.com/mobxjs/mobx-state-tree/issues/1149
export const createXStateStore = (definition: any, stores: any) => {
// Generate a configuration for xstate from store actions
const config = {
services: {},
actions: {},
guards: {},
}
Object.keys(stores).forEach((storeKey) => {
const store = stores[storeKey];
const members = getMembers(store);
const prefix = storeKey !== 'self' ? storeKey : null
members.actions.forEach((key) => {
const keyWithFirstPartRemoved = key.substring(key.indexOf('.') + 1);
const actionPath = prefix ? `${prefix}.${keyWithFirstPartRemoved}` : keyWithFirstPartRemoved
Object.keys(config).forEach((configKey) => {
if (key.includes(configKey)) {
config[configKey][actionPath] = store[key];
}
})
})
// views translate to guards
members.views.forEach((key) => {
const viewPath = prefix ? `${prefix}.${key}` : key
config.guards[viewPath] = store[key];
})
})
const machine = createMachine(definition, config)
const store = types
.model('xstate', {
machineDefinition: types.frozen(),
value: types.optional(types.frozen(), {}),
nextEvents: types.array(types.string)
})
.volatile((self: any) => ({
machine,
}))
.volatile((self: any) => ({
currentState: machine.initialState,
}))
.volatile((self: any) => ({
service: interpret(self.machine).onTransition((state) => {
self.setValue(state.value)
self.setNextEvents(state.nextEvents)
self.setCurrentState(state);
}),
}))
.views((self) => ({
matches(stateString = '') {
return self.currentState.matches(stateString)
},
}))
.actions((self: any) => ({
setCurrentState(state) {
self.currentState = state
},
setValue(value: string) {
self.value = value
},
setNextEvents(nextEvents: any) {
self.nextEvents = nextEvents
},
afterCreate() {
self.value = self.machine.initialState.value
self.currentState = self.machine.initialState;
self.service.start()
},
send: self.service.send
}))
return store.create({
machineDefinition: definition
})
}
// Prefixes all the methods with their types 'actions.', 'guards.', 'services.'
export const convertActions = (actions, configKey) => {
const updatedActions = {}
Object.keys(actions).forEach((key) => {
updatedActions[`${configKey}.${key}`] = actions[key];
})
return updatedActions;
}
export const convertToXStateServices = (actions) => convertActions(actions, 'services')
export const convertToXStateActions = (actions) => convertActions(actions, 'actions')
// Elsewhere
// convert to xstate services
// .actions((self) => convertToXStateServices({
// createSomething: flow(createSomething(self)),
// converted to actions
// .actions((self) => convertToXStateActions({
// someAction: () => history.push(''),
// auto converted to xstate guards
// .views((self) => {
// someAction: () => history.push(''),
// And then --
// .volatile((self) => ({
// xstate: createXStateStore(SomeMachine, {
// self,
// nestedStore: self.nestedStore
// })
// }))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment