To me magic code is code that requires a significant level of cognitive investment to understand and keep in your brain RAM. That threshold varies between people but it's always there.
In React's case, the chosen solution is not the issue here. It's a symptom of bad architecture. The library should be doing more than rendering twice to protect developers from side effects, to a point that rendering twice wouldn't be required.
That feels as bad as rebooting JBOSS servers once a day to "fix" OOM crashes. It's a hack at best and doesn't fix the real issue.
Like what?
I suppose I would think not rendering twice and some sort of warning about side effects would be beneficial, rendering twice means that you get a misleading view of what your application behavior is, I can see how people would - from an architectural perspective - consider it bad without necessarily using the framework.
Rendering twice is exactly what allows you to detect that. How would you detect a side effect if not by calling the function twice?
React's contract is that your component should be allowed to render twice without any changes so this is just upholding the invariant in dev mode.
Learn Elm and you'll see React's shortcomings clearer. They don't render twice but that is just the tip of the iceberg.
Be warned though, exiting the cave is painful.
However, how can react know what a side effect is? Hook into well known ones by hijacking the browser API? That sounds like a bad idea. Provide their own linter/compiler. That seems on the radar but I'm not sure I like the idea.
Also react doesn't stop you from doing it. You can say nah it's fine I want my side effect. It just runs the render explicitly twice in development, something that could happen in production for various reasons, so you can see what will happen.
Your proposed solution isn't very practical. A better solution would be a guarantee that render will only run once. But that's not how react has ever worked. There are other frameworks that do that. As with anything, there'll be a trade-off.
That's not a contradiction to it being bad magic.
In fact, this being a pragmatic solution is a strong, though not conclusive, indicator that the underlying abstractions are not fit for purpose.
And the fact that lots of pragmatic choices led to those abstractions that are not fit for purpose also is an indicator that something about the starting point or the road is not right. Or both.
You say that, but react is very successful, indicating that it's abstractions are working just fine for many people. Maybe we'd say they're not perfect but they fit it's purpose. It might not be my favourite frontend library to work with, but it's the one I'd be most likely to choose for commercial work because it does work well, and high use means that people have explored many solutions to problems with it.
React is an implementation, not the underlying abstraction. With react it's not even clear what the underlying abstraction is.