Repository navigation
What is the recommended way to provide an XState machine instance that depends on external props? #5346
Replies: 2 comments 1 reply
|
Before providing a detailed answer, would it be possible to migrate to XState v5? |
|
While you sort out the v4 vs v5 question with David, one thing worth knowing regardless of version: the input/snapshot you pass to a Provider (or useMachine/useActor) is only ever read once, on mount. I checked useActorRef in xstate-react (packages/xstate-react/src/useActorRef.ts): let [[currentConfig, actorRef], setCurrent] = useState(() => {
const actorRef = createActor(logic, options)
...
})That is a lazy useState initializer, React only calls it on the very first render. So whatever input/snapshot were inside options at that moment gets baked into the actor for its whole lifetime, changing those props on later renders does nothing, the actor keeps running with whatever it started with. That matches your "stable across renders, recreated only when necessary" requirement pretty directly actually, since there is no reactive path from props to a running actor's input, the only way to force a genuinely new actor with new input is to make React unmount and remount the Provider itself, which means changing its key prop when one of the props that should reset the machine changes: <ConnectMachineContext.Provider
key={entryType} // change this when you want a fresh actor
options={{ input: { portfolioConnect: portfolioConnectItem }, snapshot: portfolioConnectItem.persistedSnapshot }}
>
...
</ConnectMachineContext.Provider>Leave key alone for props that should NOT reset the machine, only tie it to whatever should trigger a full reset (your note vs message mode switch, sounds like). This is a React pattern more than an XState one, but it is the actual mechanism available here either way, v4 or v5. |


Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Hi everyone 👋
I'm working on integrating an XState machine (
tagMachine) into a React project and want to clarify the best approach for initialising and providing an interpreter instance based on external props (e.g.,tags,isNote,onTagsUpdate). My goal is to keep the interpreter:Ensure a stable machine instance
We want the machine to remain stable between renders, unless a meaningful prop change requires resetting the machine (e.g., switching from "
note" to "message" mode)Here I use
useMemoto memoize andinterpret(...).start()directly:Possible Pros:
Possible Cons:
interpreteris created during render, which might go against React best practices?Here, I use useInterpret from
@xstate/react, and rely onkeyat the component level to force remounting the provider:Possible Pros:
useInterpretstarts the service in a React-safe way (in anuseEffect)?keychanges - no manual stop needed?Possible Cons:
keyimplementation)Why I moved away from useInterpret initially
I initially used
useInterpretdirectly, but found that it does not recreate theinterpreterwhen the machine config/context changes. The interpreter remained the same across renders. This meantselectedTags,entryType, etc., were only set on initial mount, unless I manually sent sync events.This led me to consider using
interpretdirectly insideuseMemo- until I realised that doing so might introduce render-phase side effects and potential memory leaks.❓ My Questions
useInterpret+keythe recommended way to safely recreate interpreters with new context/config?Any thoughts, insights, or best practices would be super appreciated 🙏
Thanks in advance!
All reactions