Turns out static pages with JS on top can be even worse than badly done SPAs - especially when the server infrastructure is lacking and a single request takes a significant amount of time.
On the one hand you have a shared-nothing architecture that serves HTML and can enjoy varying forms of caching.
On the other you have an application that still depends on a server, but replaces all of that simplicity with a stateful client-server architecture that requires _more_ infra to manage, at a greater expense.
Just about every programming language has also a web framework implemented in it, so if you want to create a full-stack web app, the range of options is just dazzling, which results in a highly fragmented ecosystem.
Meanwhile on the front-end there's essentially one language(with different flavours) and 3-4 leading frameworks, so once a developer has these covered, they can be hired to do anything in this part of the stack.
The overall result is that it's easier to scale work if you offload it to the front-end, because by the time you found your <insert highly specific language/framework combination>, your front-end developers would have done most of the work twice over.
There are many more reasons why you would want a SPA instead of a full-stack app, but this is one of the strongest.
The same folks who fail to understand that and just recklessly add dependencies aren't going to craft a nice static HTML site with smart, minimal, performant JS; they just don't have the chops for clean solutions. The medium barely matters.
I was there when AngularJS was still this revolutionary new framework and back then any sufficiently sophisticated form would eventually become unmaintainable.
Can you expand on this?
I have created entire applications which are operating off of a single React context, and then also created some generic components which themselves expose a provider to their managed subtree. I have not faced any issues or "coupling" between these layers; I just pass props from the top-level context to the top of another subtree / component, and it works as I would expect.
I don't rely super heavily on derived state, but I find useEffect and useMemo to both be quite simple and effective when I need side effects or to memoize a piece of derived state to prevent throttling.
What situations am I missing that introduce these issues you talk about? How does Redux solve them more cleanly?