Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 18 additions & 8 deletions src/machines/appMachine.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { useSelector } from '@xstate/react'
import type { ActorRefFrom } from 'xstate'
import { createActor, setup, spawnChild } from 'xstate'

import { createSettings } from '@src/lib/settings/initialSettings'
Expand All @@ -14,9 +13,14 @@ const appMachineActors = {
} as const

const appMachine = setup({
types: {} as {
children: {
auth: typeof AUTH
settings: typeof SETTINGS
}
},
actors: appMachineActors,
}).createMachine({
/** @xstate-layout N4IgpgJg5mDOIC5gF8A0IB2B7CdGgAoBbAQwGMALASwzAEp8QAHLWKgFyqw0YA9EAjACZ0AT0FDkU5EA */
id: 'modeling-app',
entry: [
spawnChild(AUTH, { id: AUTH, systemId: AUTH }),
Expand All @@ -29,18 +33,24 @@ const appMachine = setup({
})

export const appActor = createActor(appMachine)
export const authActor = appActor.system.get(AUTH) as ActorRefFrom<
typeof authMachine
>
/**
* GOTCHA: the type coercion of this actor works because it is spawned for
* the lifetime of {appActor}, but would not work if it were invoked
* or if it were destroyed under any conditions during {appActor}'s life
*/
export const authActor = appActor.getSnapshot().children.auth!
Copy link
Contributor Author

@farskid farskid Mar 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Casting is safe here because, when the state snapshot is available, appMachine will have already initialized all spawned children.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice. If these were invoked actors we'd have to be more cautious with the cast here, or if they were spawned in a more ephemeral way? I just want us to remember that if we make use of other patterns as we break apart the modeling machine, for example.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, if they were invoked, they'd legitimately be available partially depending on the state. Even spawned actors can be partially unavailable too. It's safe here because we have child actors that are spawned in the root state in the entry action meaning it's the first action that runs before the the first state transition happens in the machine so in other words, these spawns are always available.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I added a "gotcha" comment here

export const useAuthState = () => useSelector(authActor, (state) => state)
export const useToken = () =>
useSelector(authActor, (state) => state.context.token)
export const useUser = () =>
useSelector(authActor, (state) => state.context.user)

export const settingsActor = appActor.system.get(SETTINGS) as ActorRefFrom<
typeof settingsMachine
>
/**
* GOTCHA: the type coercion of this actor works because it is spawned for
* the lifetime of {appActor}, but would not work if it were invoked
* or if it were destroyed under any conditions during {appActor}'s life
*/
export const settingsActor = appActor.getSnapshot().children.settings!
export const getSettings = () => {
const { currentProject: _, ...settings } = settingsActor.getSnapshot().context
return settings
Expand Down
Loading