-
I have a machine that can enter a state that invokes a service which needs to be cleaned up, and I'm having a hard time modelling it. Outside XState, the service works like this: const tabLock= new TabLock()
// acquire a lock:
tabLock.acquire() // returns a Promise<void> that fulfills on success and rejects on failure
// release a lock
tabLock.release() // synchronously releases the lock
// destroy all resources (eg BroadcastChannel)
tabLock.destroy() // in react:
function useTabLock() {
const [tabLock] = React.useState(() => new TabLock())
React.useEffect(() => {
// cleanup tabLock
() => tabLock.destroy()
}, [tabLock]
return tabLock
} In XState, I initially did this: const machineCreator = () => createMachine({
context: {
tabLock: new TabLock()
},
initial: 'idle',
states: {
idle: {
on: { ACQUIRE: 'acquiring' }
on: { DESTROY: 'destroy' }
},
acquiring: {
invoke: {
src: async (ctx) => {
await ctx.tabLock.aquire() // first acquires a browser-level lock
await acquireNetworkLock(ctx) // acquires network-level lock
},
onDone: 'locked',
onError: 'idle'
}
},
locked: {
on: { RELEASE: 'releasing', }
},
releasing: {
invoke: {
src: async (ctx) => {
await releaseNetworkLock(ctx) // releases the network-level lock
await ctx.tabLock.release()
}
},
onDone: 'idle'
}
},
destroyed: {
type: 'final',
entry: (ctx) => ctx.tabLock.destroy()
}
}
}) The above however breaks when the user navigates away without going through a "release." |
Beta Was this translation helpful? Give feedback.
Replies: 3 comments 12 replies
-
Edit: There is a better solution: #1825 (comment) Original below: The one solution I found was this: const machineCreator = () => createMachine({
context: {
tabLock: new TabLock()
},
activities: ['tabLockDestroyer']
// ...
}, {
activities: {
tabLockDestroyer: ctx => () => ctx.tabLock.destroy()
}
}) It feels somewhat bludgeoning, but maybe it's the only way? |
Beta Was this translation helpful? Give feedback.
-
It only seems hard because you're using Consider something like this instead: invoke: {
src: (ctx, e) => (sendBack, receive) => {
new Promise(async () => {
await someLock.acquire();
});
return () => someLock.destroy();
}
}, |
Beta Was this translation helpful? Give feedback.
-
@Andarist suggested the proper solution here: const machineCreator = () => createMachine({
context: {
tabLock: new TabLock()
},
exit: (ctx) => ctx.tabLock.destroy()
}) |
Beta Was this translation helpful? Give feedback.
@Andarist suggested the proper solution here: