With latest version of xstate 4.13.0 and @xstate/react 1.0.1, seems useMachine is still triggering xstate.init event twice, hence causing react to render twice too.
However, actions were not triggered twice.
https://codesandbox.io/s/adoring-wave-m62k7?file=/src/App.js
https://github.com/davidkpiano/xstate/issues/1211
https://github.com/davidkpiano/xstate/discussions/1448
This is due to using strict mode.
@davidkpiano Just to make sure we are on the same page, here's the versions I'm using, and I'm not using React.StrictMode, dose running twice still sounds like working as intended?馃
"dependencies": {
"@xstate/react": "1.0.1",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"xstate": "^4.13.0"
},
This is due to using strict mode.
@rjdestigter Just a quick note that I'm using react v16.13.1 without React.StrictMode, and I believe current mode isn't enabled in this version yet hence it might not be the cause?
Here's the code pasted from the repo I provided.
return (
<div className="App">
<button
style={{ width: "200px", height: "100px" }}
onClick={(_) => send("TOGGLE")}
>
{active ? "Start" : "Stop"}
</button>
<h3>
<code>Toggled {count} times</code>
</h3>
</div>
);
@coodoo Please prepare a runnable repro case of the situation you are experiencing and I will happily look into it to assess it is a bug or not and if not I will explain what is happening. It's hard to tell that based on random code snippets alone 馃槈
Yup, that's the repo I provided in the original post for:
https://codesandbox.io/s/adoring-wave-m62k7?file=/src/App.js
Note: The repo I provided doesn't use concurrent mode and StrictMode but still suffered the rendering twice issue, everything below is for pure discussion.
I dug deeper and read this article, now realize in concurrent mode a component might be rendered multiple times but only one of them will be committed.
Hence in a way useMachine was indeed working as intended, but isn't it pretty annoying to see each trace message twice? Which most definitely will make debugging a lot harder, any thought on how to fix this?
Yup, that's the repo I provided in the original post for:
That demo is using the StrictMode though. Check it in index.js.
Which most definitely will make debugging a lot harder, any thought on how to fix this?
It's impossible. This is how React plans to work in the future and StrictMode is there to help you catch errors in your code before production. You just can't reliably execute side-effects in render and console.log is a side-effect.
That demo is using the
StrictModethough. Check it inindex.js.
Oh darn, they sneaked in that by default...my bad馃槄
Which most definitely will make debugging a lot harder, any thought on how to fix this?
It's impossible. This is how React plans to work in the future and
StrictModeis there to help you catch errors in your code before production. You just can't reliably execute side-effects in render andconsole.logis a side-effect.
Right, I was afraid this will be the answer, so how do you guys plan to deal with this when debugging? Not using console.log at all?馃ズ
As a side note, is it possible to treat xstate as a side effect and invoke it inside useEffect?
Right, I was afraid this will be the answer, so how do you guys plan to deal with this when debugging? Not using console.log at all?馃ズ
It's not our thing to solve - it's not related to how we implement anything. It's purely a React thing, so we can't act on it - even if we'd like to.
As a side note, is it possible to treat xstate as a side effect and invoke it inside useEffect?
We already start machines within useEffect:
https://github.com/davidkpiano/xstate/blob/8b05b57096157e5bbcc9594f2e434da9839ebce7/packages/xstate-react/src/useMachine.ts#L232
It's not our thing to solve - it's not related to how we implement anything. It's purely a React thing, so we can't act on it - even if we'd like to.
Yep, I鈥檓 fully aware there鈥檚 probably nothing could be done on xstate鈥檚 side, just wondering when building applications with xstate, how do you debug? For that now we know console.log can鈥檛 be a reliable measure.
It all depends on what kind of a problem you are trying to debug - I usually mix logging with using the debugger, I rarely need to use logging within renders, even when I don't use XState, so it's hard for me to relate here. You could try to set up a logger within onTransition that you would set up within useEffect.
Just a quick note for people in the future seeking to eliminate the double logging message issue while rendering once concurrent mode become default, here's a _naive_ solution (trying) to fix that, usage below.
Key concept is although there might be multiple rendering instances at the same time, only the one that's committed gets to print out messages.
const useLog = () => {
const ins = [];
const log = (...msg) => ins.push(msg);
function reset() {
ins.length = 0;
}
useEffect(() => {
ins.forEach((l) => console.log.apply(null, l));
reset();
return () => reset();
});
return log;
};
```js
const App = props => {
const log = useLog()
log('some thing') // this won't print twice
return 'foo'
}
Most helpful comment
This is due to using strict mode.