xstate: useMachine stops working after fast refresh
Description
The useMachine hook works as expected in react native when the app loads for the first time, then after a “fast reload”, the hook stops updating the state, send still works and the machine completes actions in the background, but the state is never updated.
Expected Result
When performing a “fast reload”, the useMachine hook should still return the current state.
Actual Result
After “fast reload” the useMachine hook never updates even though the state machine is still working.
Reproduction Should be fairly easy to reproduce using react native and performing a fast refresh.
Additional context xstate: 4.7.8 xstate/react: 0.8.1 react: 16.9.0 react-native: 0.61.5
About this issue
- Original URL
- State: closed
- Created 4 years ago
- Reactions: 12
- Comments: 26 (14 by maintainers)
This isn’t limited to the fast refresh of React native. I’ve been using fast-refresh in my CRA project for quite a while now using https://github.com/esetnik/customize-cra-react-refresh. I had to manually refresh the browser on every change as the machines stopped handling events. That being said, the state was always preserved correctly.
@Andarist if you would you rather debug this with normal React, I can create a CRA fast refresh Codesandbox for you? 😊
I’m having the same problem in next.js now they’ve started using fast-refresh. Here’s a tiny example of it breaking using next: https://github.com/idlefingers/next-xstate-fast-refresh-bug
Hope that helps!
I’m too familiar with Fast Refresh design, does anyone have a link to its technical details?
I’ve investigated your repro (thanks for it!) with our
@xstate/react@next(so a different one that you have installed there). What I’m observing is that our effect gets “remounted” - its cleanup (service.stop()) is being called even though[]is passed as inputs so it should only be cleaned up when unmounting. However, I’ve seen the React team mentioning that in the future this might change and effects could potentially “remount” like this at different occasions as well. How this will work is still an unknown to me so I wouldn’t like to plan ahead for it too much, unless we would be able to figure this out quickly.So the problem here is that we stop the machine when fast refresh happens - things like state & refs are being preserved as the whole component is not being remounted. I’m honestly not sure what we should do here - we could try to restart stopped machine by supplying initial state to it (which we should be able to read from the stopped service), but this really sounds like a can of worm. We ideally would like to have our effect not being remounted as we’d like to have a guarantee that its lifetime (and thus our machine’s) is exactly the same as the component’s lifetime.
So I ended up trying out the hook that was used in this youtube video https://youtu.be/73Ch_EL4YVc I rewrote it to typescript, and that ended up working fine. Here’s the code for anyone interested:
My biggest suspicion lies with the use of
useConstantinstend ofuseMemo, but I haven’t been able to confirm that yet.I’m afraid that we are not React Native developers on a daily basis and fast refresh is not yet available on the web, so we don’t quite know how it works exactly and what kind of constraints does it impose. From what I understand it should “just work”, but apparently it doesn’t. Could you dig into this deeper to provide us more information about the problem?
I’ve confirmed that the fast refresh bug in this repo no longer occurs with the latest version of
@xstate/react(version 1+).@gaearon I’ll be filing the issue in the following days. Thanks for the brainstorming offer 👍
@Andarist I’ve changed the
useConstantto:It worked like a charm.
latestStateRefwas used to enable rehydration.Here’s the full hook if you want to test:
@CodingDive It’s the same as I’m getting with react native. I also updated and tested with 1.0.0-rc.3 which gives me the same warnings as I mentioned earlier.