#getPersistedSnapshot & inline child actors

1 messages · Page 1 of 1 (latest)

ruby trout
#

Hi everyone. I'm coming back to some state persistence code I've had commented out during the beta and I see this error:

Error: An inline child actor cannot be persisted.
at getPersistedSnapshot (raise-c8b9d708.development.esm.js:2331:13)

On this announcement: https://stately.ai/blog/announcing-xstate-v5-beta#deep-persistence

I see: In XState v5 beta, actors are now deeply (recursively) persisted. Invoked/spawned actors will be persisted, as well as actors invoked/spawned from those actors, and so on.

I have a number of invoked actors similar to this:

        systemId: 'mySysId',
        id: 'myId',
        src: myImportedMachine,
      },

Is this not allowed for persistence? If not, what can be changed to allow persisting? The main reason I have these child actors is to allow system communication.

We’re excited to announce the beta release of XState v5 and related packages after many years of development.

crystal scaffold
#

Change the src to a named source

#

cc. @uneven stream

ruby trout
#

ok, thanks. I'll try that today & respond back here afterward.

ruby trout
#

I have a machine that is persisted separately & it has an invoke with inline like this:
src: fromPromise(....)

This machine has been persisting fine for some time.

In the erroring machine, I've moved the imported machines down to the implementations.actors argument to createMachine and referenced them by string in the one that is erroring and I still see the error. There are some additional Promise src, would those affect it even though the other machine mentioned above persists ok?

crystal scaffold
#

I'd have to see a full code example, if you can

ruby trout
#

Hmm, I'd prob need to send you some code directly.

I'm looking at the location that throws the Error: if (typeof child.src !== 'string' && (!options || !('__unsafeAllowInlineActors' in options)))

Perhaps the fromPromise that are 1 level deep must use the string version.

#

I'll try changing those

ruby trout
#

hmm, this does not solve it either

#

Placing a breakpoint in devtools I see it has an id of "x:31", maybe this is a spawned child.

ruby trout
#

ya, thats it, got past one. Is it possible to exclude a child from the persisted state automatically?

crystal scaffold
#

What do you mean by automatically? How would that look?

ruby trout
#

For example a spawned fromPromise that just performs some action or I have some invoked children that cache some data that I dont want to persist, but they are part of the system so they can have envets forwarded to them. I've somewhat considered refactoring to separate them and communicate some other way, but can't spend the time on it. I'm looking at manually pulling them out of the children in the persisted snapshot based on a naming convention. I suppose it could be an option, like excludeFromPersistedSnapshot.

Or perhaps better yet, it might be nice if actors could join a system without becoming a child.

#

I am actually seeing something now that may mean I shoudn't manually exclude children. They don't get created by the invokes when loading the sate back in, so they don't exist. Perhaps that means being able to join a system, but not as a child would work out better?

crystal scaffold
#

Maybe a future fromActor may help:

// TENTATIVE API
context: ({ input, spawn }) => ({
  notifier: fromActor(input.notifierActor, {
    systemId: 'notifier'
  })
})
#

Although there is a bigger thing to consider: an Order has many Items... where are those items stored?

#

If you architect your app so that a central state machine stores the state of those items (pretty simple - will feel just like Redux or Zustand), then you can invoke those system-wide actors and have them shared pretty easily

#
const appMachine = createMachine({
  context: {
    orders: [],
    quotes: []
  },
  // powerful feature: these will be shared with the spawned actors!
  invoke: [
    { src: 'notifier', systemId: 'notifier' },
    { src: 'logger', systemId: 'logger' }
// ...
#

And then, say you have this:

const orderMachine = createMachine({
  initial: 'pending',
  context: ({ input }) => ({
    orderId: input.id
  }),
  on: {
    ship: {
      target: 'shipping',
      actions: sendTo(({ system }) => system.get('notifier'), { type: 'notice', message: 'Shipping!' })
// ...
#

In the appMachine, top-level:

// ...
on: {
  createOrder: {
    actions: assign({
      orders: ({ context, spawn }) => orders.concat(spawn(orderMachine, {
        input: { id: 'abc123' }
// ...
ruby trout
#

appMachine & orderMachine, can they be part of the same system even though created independently?

crystal scaffold
#

Yes, think of machines as "pure functions" (or roughly in that category)

#

It's actors that are part of the same system

ruby trout
#

Thanks for the help. I will noodle on this for a while. I may end up coming back to it later because there is so much other work to do still.

uneven stream
#

Note that invoked actors should be persistable even if they are "inline" (as opposed to being referenced with a string). We can't do the same for spawned ones though (but we might think about some improvements in this area for spawnChild)

cerulean cosmos
crystal scaffold
cerulean cosmos
# crystal scaffold Which whole code? The code in the message you're replying to is pseudocode

Could you provide an example of how to use getPersistedSnapshot with an actor that has a child actor or children actor array? I've looked at the docs on https://stately.ai/blog/announcing-xstate-v5-beta#deep-persistence, but the code segments there are incomplete. I'm looking for a complete example."

We’re excited to announce the beta release of XState v5 and related packages after many years of development.

crystal scaffold