React deserves credit for bringing fresh thinking into the JS framework space but there's a tremendous amount of room for improvement.
* = subjective and there are a few rare exceptions
Generally you should only need to memoize a few things, and if you find that your app becomes brittle due to the amount of memoization you’re doing you probably are not handling state in the right way. If you’re using Redux that’s a common reason for this; don’t use Redux (is my advice). And yes, I’ve worked on several large enterprise React apps at successful startups.
I think what OP was suggesting is that you don't have to memoize at every level of the tree / where it doesn't make sense.
That's not to say hooks are much better, but I find the overall mental model to be fairly simple to understand, and I don't often find myself bumping up against React's edges.
In this example the second $ won't trigger the first $:
let a = 1;
let b = 1;
$: if (a > 0) { b += 1 }
$: if (b > 0) { a += 1 }
With React's useEffect one can easily falls into the trap of an infinite re-rendering: let a = 1;
let b = 1;
useEffect(() => {
b += 1;
}, [a]);
useEffect(() => {
a += 1;
}, [b]);
Great framework are supposed to prevent this kind of loophole.https://github.com/sveltejs/svelte/issues/6730
https://github.com/sveltejs/svelte/issues/6732
So basically, reactive blocks are only allowed to run once per tick; if a dependency changes after a block has run, too bad, the block won't run again. That is really shocking to me. (I won't say more due to my inexperience with Svelte...)
It's a shame because it's an otherwise lovely framework.
What Svelte is doing is simply covering up the flaws in your code by short circuiting an infinite loop scenario which has been coded into your app. This will have unexpected side effects, however.
That being said, I think useEffects is far too overloaded in React, and is currently used for completely orthogonal purposes.
At he very least it’s used for the following completely unrelated purposes.
1. Component setup and graceful dismantling. 2. Syncing with external state. 3. Running code specific to a certain prop being changed.
I think the React team would be highly justified if they created a separate hook for #3 at least. Maybe a usePropsChanged hook that is called when one of the props change, and provides an isChanged function that you can pass a prop to, to check if it’s actually changed (I’m sure someone can come up with a better design than I have in the process of writing this comment).
That would be a semantic which would actually allow you to avoid the code flaw that your code above includes, as opposed to brute force papering it over like Svelte appears to do.
It's not a fault in the framework, it's a fault in their understanding, where they don't know how to implement features in O(N) lines of code instead of O(N^2).
The hint is in the name: you're supposed to think in terms of formal functional Effects that mount and unmount independently and compose orthogonally.
The result will be a code base that remains maintainable even though you've kept on adding more nuanced features over months and months.
Also, your snippet doesn't cause an infinite loop at all because modifying local variables does not trigger a rerender. If you had written setA / setB, sure, it would... but I think this perfectly illustrates the explicit vs implicit distinction.
Great frameworks don't trap developers in local maxima.
Do you have any links where I could read more about this? Or examples?
A couple years ago, that feature used to work similar as useEffect, so I got used to use it for transitions, and now is biting my ass because the Svelte team are breaking my transition use-cases
I used to use "$" like useEffect, but now I don't know how to use it anymore
And Svelte still bites my intuitions when dealing with complex objects, most of the times I've manage solve it by separating the code into multiple components, but it's not clear why that happens
I think the dev community does really poorly with these kinds of abstractions that are very simple but completely unintuitive.
I place a lot of the blame on the React team too. When hooks came out, their docs were absolutely terrible. They probably still are. They were full of shitty editorialising like "we created hooks because we found classes were too difficult for people to understand!", which made every dev I worked with feel insecure for not immediately knowing how to write super awesome clean code.
No, some people actually get them and there are way much better alternatives[1].
1. http://intelligiblebabble.com/compose-from-first-principles/
That being said, it's a relatively small complaint.
I actually read this as a compliment considering Tarantino himself spoke in an interview about how his non-linear storytelling style comes from the way novels he'd read were written.
So where should we try to draw the line at what is truly the kind of "app" that deserves this much JavaScript, versus a "glorified website" that management wants to see lots of interactivity/animations/etc.?
In my career, I've struggled far less with choosing a JavaScript framework for front-end interactivity, and far more with justifying how much JavaScript goes into Web experiences these days, without any concern for the end-user performance impact or other impacts that may not be as obvious, e.g. accessibility.
Assuming you're asking more along the lines of "how does it work internally?", these are my usual recommendations:
- My own extensive post "A (Mostly) Complete Guide to React Rendering Behavior": https://blog.isquaredsoftware.com/2020/05/blogged-answers-a-...
- Related, "When does React render your component?", which looks more at the source level checks: https://www.zhenghao.io/posts/react-rerender
- Dan Abramov's "A Complete Guide to useEffect" https://overreacted.io/a-complete-guide-to-useeffect/
- Shawn Swyx Wang's talk "Getting Closure on React Hooks" that builds a mini version of React hooks: https://www.swyx.io/hooks/
- Rodrigo Pomber's excellent "Didact: Build a Miniature React with Hooks": https://pomb.us/build-your-own-react
What all these React spinoffs seem to be doing is unlearning the lessons encoded in the React architecture and replacing them with more magic by "helpfully" applying state changes immediately instead of in a controlled fashion. But this is going to create mixtures of old and new state, which creates subtle bugs down the line.
Discussions about VDOM also seem mostly pointless because it is clear to me most people's mental model of how React renders is flat out wrong. If you are redundantly touching the DOM, that means you haven't memoized your components, or you are needlessly making copies of the same state. All of these have far bigger performance implications. In fact, even without memo(...), React renders can halt early.
Quoting from the Use.GPU/Live docs, which is a true React extension with the same semantics:
"If a component renders the exact same JSX object as before, React will not re-render that child, even if the child is not memoized.
So there is a hidden semantic in React: when you construct a JSX expression anew, you are requesting that those children be re-rendered unless memoized. But if you reuse a JSX expression from before, you are implicitly giving permission to ignore any unchanged children."
This is crucial to understand when stacking context providers.
I don't get the sense that React derivatives have been used to build e.g. desktop-class apps. They mostly seem optimized for web sites.
While it is true that React does a lot of things at run-time instead of build time, this is necessary for building truly data driven apps. JSX is really Lisp quoting in disguise. If you can't recognize this, you should probably brush up on the fundamentals.