I like the article, but it gets some things subtly wrong.
> To grossly oversimplify things: React assumes that your entire virtual DOM tree needs to be rebuilt from scratch, and the only way to prevent these updates is to implement useMemo
Not quite, on a state update, it rebuilds the component that was updated and all of its children. Not the entire virtual DOM; old versions of Angular did this, but it was wasteful.
useMemo doesn't prevent that, but React.memo can (useMemo has a different role; it lets you choose when to recompute or recreate a normal JavaScript object. But on its own it won't stop rerendering of child components!) [0]
This invalidates some of their assumptions. The reason why React isn't "push-only" isn't because it does that, it's because it sometimes buffers updates instead of always pushing them immediately. In fact, other frameworks like ~~Svelte also aren't "push-only" and hence not strictly reactive~~! [edit: this is no longer true after Svelte v5, see discussion below] (Funnily enough, OP uses an article as a source that explains this correctly [1], but it seems they took the wrong lesson from it).
The reason why signals are so cool is because the framework knows for any given state change which exact attributes in the DOM need to be re-rendered, even more specifically than "the element and all its children". But this neither implies reactivity nor the other way around. The two concepts are orthogonal.
Anyways, kudos to the author for diving into this so deeply!
[0] useMemo is useful in combination with React.memo sometimes, as the latter compares objects shallowly/by reference instead of their contents, so useMemo can be used to only recreate shallow references if its contents changed. You could probably also reimplement React.memo with useMemo, but you probably shouldn't.
[1] https://dev.to/this-is-learning/how-react-isn-t-reactive-and...
I did indeed mix up `useMemo` and `React.memo` – fixed it in the post.
You're right, I am skipping a lot of details (hence "to grossly oversimplify"). I know that React doesn't invalidate the whole tree, but it does in the worst case. Maybe I should add a note about that.
Svelte not being truly reactive makes perfect sense, but in Svelte v5 my understanding is that "runes mode" does exactly that. This is what I mean by "moving in that direction."
> AFAIK there's no magic to React.memo. It's basically a shorthand for useMemo that takes the props as the dependency.
Pedantic note: this isn't quite true. memo() also allows a second `arePropsEqual` argument that useMemo doesn't have. Also, memo() compares individual prop values, while useMemo() can only look at the whole props object (which would be "fresh" on every render -- it's a diffferent object, even if it has the same values). So it's not like you can easily reimplement memo() via useMemo(). But of course, conceptually they are pretty close :)
> “Also, memo() compares individual prop values, while useMemo() can only look at the whole props object”
Passing “Object.values(childProps)” as the dependency array for useMemo should do the same thing.
But yeah, there are good reasons to use React.memo for convenience with props. It’s not fundamentally different though, and you can definitely useMemo() for caching components when more convenient.
> You can definitely use useMemo with JSX elements to prevent child components from being re-rendered too often
Only if those child components are memoized. By default, whenever the state of a component changes, React will rerender the entire subtree. The only time it doesn't is when a child component is memoized (React.memo) AND the props haven't changed. Utilizing useMemo and useCallback is how we prevent non-primitive props from being recreated unnecessarily
React actually has a little-known "same element reference" optimization. If your component returns the exact same JSX element reference in the same spot in consecutive renders, React will bail out of rendering that subtree, regardless of whether or not the child component is wrapped in `React.memo()`. This allows the parent component to control the behavior. So yes, `useMemo` would be how you do that:
I see what you mean. I'm a little shocked the docs have an example of it being used in this way. Using `useMemo` in this way is generally considered a bad practice and a "hack". The new version of the react docs does not have an example of useMemo being (mis)-used in this way
> To grossly oversimplify things: React assumes that your entire virtual DOM tree needs to be rebuilt from scratch, and the only way to prevent these updates is to implement useMemo
Not quite, on a state update, it rebuilds the component that was updated and all of its children. Not the entire virtual DOM; old versions of Angular did this, but it was wasteful.
useMemo doesn't prevent that, but React.memo can (useMemo has a different role; it lets you choose when to recompute or recreate a normal JavaScript object. But on its own it won't stop rerendering of child components!) [0]
This invalidates some of their assumptions. The reason why React isn't "push-only" isn't because it does that, it's because it sometimes buffers updates instead of always pushing them immediately. In fact, other frameworks like ~~Svelte also aren't "push-only" and hence not strictly reactive~~! [edit: this is no longer true after Svelte v5, see discussion below] (Funnily enough, OP uses an article as a source that explains this correctly [1], but it seems they took the wrong lesson from it).
The reason why signals are so cool is because the framework knows for any given state change which exact attributes in the DOM need to be re-rendered, even more specifically than "the element and all its children". But this neither implies reactivity nor the other way around. The two concepts are orthogonal.
Anyways, kudos to the author for diving into this so deeply!
[0] useMemo is useful in combination with React.memo sometimes, as the latter compares objects shallowly/by reference instead of their contents, so useMemo can be used to only recreate shallow references if its contents changed. You could probably also reimplement React.memo with useMemo, but you probably shouldn't. [1] https://dev.to/this-is-learning/how-react-isn-t-reactive-and...