If anyone is curious as to why, I believe it's the only way that React can "poke" your code to see whether it really is side-effect free, and therefore concurrent mode/suspense compatible. The idea is that React can re-render your component whenever and however often it likes whilst getting the same results for the same input state, without this assumption, new patterns such as concurrent mode and suspense become very difficult to implement. Double rendering is a rudimentary dev-time test to see whether the output is actually the same to try and help you avoid bugs later on (data races for example), without enforcing a language, such as ReasonML or Elm. Alternatives would require really complex static analysis or strict ESLint rules. There may be other reasons as well.

Sometimes there's a need for side effects, the DOM is very stateful and unfortunately React is not quite fast enough for smooth animations at 60fps on most devices, it's also a lot easier to use global variables (no judgement) rather than properly organising state hooks and component props.

React started to move away from OOP and more towards FP, starting with hooks and function components. One of the driving reasons behind this was that function components could be minified a lot better than classes. Reducers, side effects, pure, memo, are terms that are more known in the FP land. I also think it gave them a lot more control as a framework to implement patterns such as concurrent mode as functions are stateless unlike classes, moving the state up into the React framework itself. This means several versions of the state can exist at once, and React just runs them through your function to generate the view. Side effects introduce unpredictability and nondeterminism using this pattern.

This FP style can be confusing to new programmers (anyone feel like trying to explain useEffect() to a 5-year-old? Anyone?), especially those that are used to global variables or manipulating the DOM directly (JQuery), but understanding the OOP render cycle had its own learning curve. Frameworks such as Elm are met with much love, even if React's hook-based approach is a little different.

Small aside, I also think that's where useEffect() got it's name from: side effects. It's where you put all those nasty network calls and setTimeout()s, and then scratch your head when you forgot to cancel/unregister them when the component unmounts and React complains that you tried to change the state on an unmounted componment. Suspense (hence the need for side-effect free functions, hence this double-rendering problem) should help with avoiding putting "if (stillMounted) { ... }" everywhere in useEffects with async code by giving React more control over the render/update process.

Full disclaimer, I haven't actually written React in a while, not since I found out about Svelte, I'm curious, how to authors of libraries such as React/Framer Motion handle the double rendering issue when interacting with the DOM directly? Other frameworks that I know of that avoid the complexities of concurrent mode/suspense either very stricly enforce this FP style, such as Elm, or try to be fast enough to forgo the virtual DOM and embrace mutable state, events and two-way data binding such as Solid and Svelte.