Xstate: Accessing Invoked Child Machine State In React Typescript

Created on 15 Jan 2020  路  10Comments  路  Source: davidkpiano/xstate

Description

I have two machines, a parent machine and a child machine. The parent machine invokes the child machine on start and the child machine keeps running indefinitely between three states. How can I print out the child machine麓s current value using react?

Expected Result
I expect to be able to subscribe to invoked child machines state transitions somehow so that I can update my react ui with the information.

Actual Result
Accessing the child machine does not return the invoked instance nor an interpreter, but instead some metadata.

Reproduction
See a full sample here, notice how the child machine state is rendered N/A even though the invoked child machine is running (see console output):

Additional context

I've extracted this issue from a comment on 772. As far as I can tell #772, #664 , #938 may or may not be related issues.

This is the package.json versions:

    "@types/jest": "24.0.25",
    "@types/node": "13.1.6",
    "@types/react": "16.9.17",
    "@types/react-dom": "16.9.4",
    "@xstate/react": "1.0.0-rc.1",
    "react": "^16.12.0",
    "react-dom": "^16.12.0",
    "react-scripts": "3.3.0",
    "typescript": "3.7.4",
    "xstate": "^4.7.6"
bug enhancement

All 10 comments

Looking into the source code and it seems that this should pretty much work. I see the potential issue with isObservable & isMachine checks order, maybe that's the cause - but it seems to me that if that would be the case then parent->child communication would be broken.

Thanks for the report, I'll investigate this further today.

@Andarist any luck? I have not gotten any further.

I've managed to make it work with this - https://codesandbox.io/s/laughing-chatterjee-wemzp

state.children is not equal to service.children, so that was one of the mistakes made. I admit this is confusing a lot and we probably should do smth about it in the future. The other thing is that you have tried to access this invoked service during the first render, but it's only, currently, available after the service has been started which is in the internal useEffect. That's why I had to introduce this mounted state and split into two components. That being said - in a new version of @xstate/fsm (still unpublished) this one shouldn't be a problem. I'll add tests for such a scenario to see if it's covered.

This is actually something not implemented yet. When spawn()-ing a machine, you can pass { sync: true } as an option to synchronize its state, but you can't do the same when invoking a machine, at least not yet.

This would be an enhancement. Also, in V5, state.children will be an array, so I'm not sure we should make the change just yet @Andarist

This would be an enhancement. Also, in V5, state.children will be an array, so I'm not sure we should make the change just yet @Andarist

Yeah, i didnt mean that we should handle this right away. I would, in general, prefer to only implement new features for v5. Why it will be an array though? It seems less ergonomic.

Why it will be an array though?

~Easier to iterate, and some invoked services will not have IDs (think spawned actors).~

It will be reverted to an object in V5, so same API.

Its harder to do a lookup though, which seems useful. I would expect lookup being performed much more often than iteration for those.

As to services lacking IDs - I suspect internally we need to be able to reference them anyway, so they probably would have auto-generated IDs (not necessarily with a predictable algo), right?

Maybe the service could expose the children as an array and provide a method that looks up a particular child by id?

For now, we've decided to keep them as an object, so don't worry :) lookup still will be easy.

I'm wondering though - how exactly do you use .children property? It would be highly informative for me to get some insight on how people use it.

I'm wondering though - how exactly do you use .children property? It would be highly informative for me to get some insight on how people use it.

In my case, I have an initial _identify_ state that invokes a new machine that onDone sets a session to the parent context. Only then I transition. This child machine is plucked from rootService.children.get("identify") and injected through react props/hooks. This keeps both machines (and react trees) loosely coupled.

Hope this helps! Happy to hear about other ways to go about it.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

drmikecrowe picture drmikecrowe  路  3Comments

suku-h picture suku-h  路  3Comments

dakom picture dakom  路  3Comments

hnordt picture hnordt  路  3Comments

hnordt picture hnordt  路  3Comments