-
Notifications
You must be signed in to change notification settings - Fork 92
Description
Hey, thanks for Robot! It's great. We're using it on a project to manage some tricky online/offline sync workflows and it has simplified everything quite a bit.
I'm actually in the middle of writing a tutorial around it, and hit upon what I think is a bug, but I'll describe what's happening.
My machine implements a "confirm to delete" flow, and it has an invoke
state to call the function. Initially I had this deleteFn
declared inside the file, hardcoded. Worked fine, but I wanted to parameterize it, so I wrapped the machine in a function...
const confirmationFlow = (deleteFn) =>
createMachine({
initial: state(transition("begin", "prompting")),
prompting: state(
transition("confirm", "loading"),
transition("cancel", "initial")
),
loading: invoke(
deleteFn,
transition("done", "initial"),
transition(
"error",
"prompting",
reduce((context, event) => {
return {
...context,
error: event.error
};
})
)
)
});
And then I passed the function to useMachine:
const deleteThings = async () => { /* ... */ }
function App() {
const [current, send] = useMachine(confirmationFlow(deleteThings));
// ...
}
This creates an infinite loop because the useEffect
inside useMachine
is re-rendering every time the argument changes, which happens on every render.
One workaround is to pull the definition out...
const mac = confirmationFlow(deleteThings)
function App() {
const [current, send] = useMachine(mac);
// ...
}
But I think then the machine could be shared between multiple component instances, and can't access anything in the component scope like callbacks.
I can also work around this by passing the deleteFn
function along with an event - parameterizing at call time, instead of init time.
Mostly, though, I sorta expected that useMachine would work the same as useState, where it "latches" the first value it sees and ignores future changes. I also tried passing a function to resolve the value (like useMachine(() => confirmationFlow(deleteThings))
) but that throws an error.
I think one fix would be to change the behavior so that useMachine doesn't re-render when the passed value changes, but if that's undesirable, maybe supporting the function initializer approach would be a good in-between?