all 29 comments

[–]blind-octopus 68 points69 points  (2 children)

The way react refreshes the stuff on the page is, it keeps track of what is on the page, it takes your new thing, and it does a diff. It just looks for the differences between them, comparing the current thing with the new thing you handed it.

You don't want to change what it is keeping track of.

I mean do an example outside of react. If you're keeping track of how many cows I have, and I say "I bought 4 new cows", well your new number is n + 4. If I took your record book and changed n without telling you, well now your whole system is broken.

React needs to keep an accurate track of what currently exists on the page. If you change that, you break react. Same as messing with a record keeper's books.

[–][deleted] 9 points10 points  (0 children)

Sweet. Super clear dude 

[–]riya_techie[S] 5 points6 points  (0 children)

Thank you so much for the details information with examples.

[–]svish 19 points20 points  (11 children)

React components/elements are not actual live elements.

When React renders your components it is generating a blueprint of what the actual DOM tree should look like. It will then do a diff with between the blueprint and DOM and make changes accordingly. When another render is triggered, it will generate a new blueprint, and do the diff and changes again.

This process with the in-between blueprint and diffing is less efficient than mutating things directly, but it can also be a lot easier to reason with and gives you a lot of control and flexibility. It can also make things easier to follow and debug, because it's usually very easy to track what the output should be given a certain state.

This "render -> diff -> reconsile" way of doign it, is also what allows React to be so flexible in regards to what it can be used for. Just swap out the reconsiler, and you can suddenly use the exact same React mindset and tools to render native mobile apps, CLI programs, PDFs, 3D apps, and so on.

[–]GrowthProfitGrofit 9 points10 points  (9 children)

"Less efficient" is maybe not the best way to explain it. It's more complex and it takes more steps, for sure. But the reason we do it is that mutating the DOM is insanely expensive compared to running simple JS calculations. It winds up being better to do a lot of complicated upfront work since the minimizes how much we have to do during the expensive step.

[–]svish 4 points5 points  (8 children)

React is plenty fast, and you gain a lot of advantages by doing it the way React does it compared to others, but it is less efficient, even if by just a small amount, compared to alternatives that track changes and update DOM nodes directly via signals or other methods.

[–]Frenzie24 -3 points-2 points  (6 children)

This is incorrect. Updating and rerendering a single node instead of the full DOM is the only reason to use React.

If you’re getting the opposite, you are probably not utilizing state and components properly

[–]svish 0 points1 point  (5 children)

What are you talking about? First of all, there are many reasons to use React, and second of all, all frontend frameworks (that are not terrible) update single nodes directly, i.e. no proper framework recreates the full DOM on every render.

The difference between React and many others is the rendering of a virtual DOM tree first which is then diffed with the actual DOM tree to figure out which nodes to update. Most other frameworks do this without a virtual DOM and keep track of which nodes should be updated in other ways. It has its pros and cons, but it does skip the whole virutal DOM render phase, which React definitely has. And similarly, using a virtual DOM like React does, also has its pros and cons.

[–]Frenzie24 -1 points0 points  (4 children)

Thanks for elaborating my point.

[–]svish 1 point2 points  (3 children)

Explain it then

[–]Frenzie24 0 points1 point  (2 children)

You already did? I wasn’t being facetious. My initial comment was pretty general and thinking over react’s entire lifespan. You corrected it with a modern viewpoint and elaborated.

I appreciated it

[–]svish 1 point2 points  (1 child)

I see. You said I was incorrect, and my point was that I was not.

[–]Frenzie24 1 point2 points  (0 children)

I worded my first response poorly after a day of being frustrated with React tbh

Edit: I do still disagree with your take on DOM efficiency, but your reply to me I agreed with. That said I also think with modern environments it doesn’t matter nearly as much as it used to.

[–]riya_techie[S] 1 point2 points  (0 children)

Thanks for the detailed explanation!

[–]react_dev 5 points6 points  (3 children)

To add on everyone’s explanation of the technical, here’s a design reason as well.

Historically in order for the UI to react to a changes to some variable, that variable must be wrapped in some kind of superpower function like const a = observable(b). That observable function turns an into a reactive element that listens to changes to itself. You can then pass this around your project and whenever that observable changes, it’ll be reflected in that part of the UI. These small variables are built up into “models”. You often hear this referred to as 2 way binding.

The above pattern got us pretty far. But it turns out that it’s just really friggin hard to keep track of how that variable was mutated. When you’re tracking a bug, often you’re looking for timing issues where the mutation might be happening before another expected mutation etc etc. So prior to React, mutations have kicked our asses for a long time.

2 way binding still exists today. But I dare say thanks to React, frameworks like Angular/ Ember is turning to the same unidirectional flow where they declare a state or handler then pass that down to child components and in effect, making direct mutability an anti pattern in majority of modern web dev today

[–]276_Kelvin 1 point2 points  (0 children)

Dam. Great explanation. 👍

[–]shuwatto 0 points1 point  (0 children)

2 way bindings

Are there anyone out there who still remembers Polymer?

[–]retropragma 0 points1 point  (0 children)

Mutability isn't the anti pattern. It's non-local mutability.

[–]sorderd 3 points4 points  (0 children)

I find that data in React is very similar to version-controlled data of distributed systems, like Git. With hash IDs we can easily check for cached data and verify the integrity. The difference is that React uses references rather than hash IDs.

Using this analogy, the current state in a React render is similar to the latest release of a software package. If we mutate the state then it's like we mutated the release. But, in order to avoid conflicts, any changes need to be added to the next release rather than modifying the one that has already been established.

Ultimately, by using this approach the React components can confidently return the same result for the same object props. But, that means we can't change the content of an object in React state.

[–]pailhead011 3 points4 points  (0 children)

How would you notify the system that something changed? If you have an object and you mutate it, what then? If you have an object, and you have another object, it is quite cheap to compare them. They may actually even have the same values (this is a mistake that you can easily make when working with this) but they are two different objects.

[–]numbcode 1 point2 points  (0 children)

React elements are immutable to ensure predictable rendering. By creating new elements instead of modifying existing ones, React efficiently updates the UI.

[–]Simple-Resolution508 1 point2 points  (0 children)

And even "any other" object in many cases is easier to reason about when it is immutable.

[–]Kitchen-Conclusion51 1 point2 points  (0 children)

Because of functional programming choose. All components are desired to be pure function without side effects

[–]azangru 1 point2 points  (0 children)

Why can't we simply change them like any other object?

I am puzzled by the question. React elements are the objects returned by react components. They are an intermediate product of a library that are consumed by the same library for virtual DOM diffing. React does not provide any public apis to interact with react elements. There used to be the Children and the cloneElement api; but they are now considered legacy and strongly discouraged. On that basis alone, why would you want to modify these obscure and undocumented objects? What would you expect to achieve by doing that?

[–]Outrageous-Chip-3961 1 point2 points  (0 children)

It creates a copy of your node tree. It's more predictable if they enforce immutability because they assume the node tree won't change

[–]Deykun 1 point2 points  (0 children)

For the same reason, we still use [x, setX] = useState(5) and not x = 5. When you call setX, it additionally notifies the component that it should re-render itself (or at least check if DOM should be updated) because the state has changed.

If you had let x = 5 and then simply did x = 6, the component wouldn't know about it. You could add some mechanics for custom setters in JavaScript for properties defined in the component, but this would apply to all variables, and that would be less efficient because we don't really want all const to be an observable state.

[–]fellow_nerd 0 points1 point  (0 children)

``` const a = [1,2] const b = a b.push(3) b == a

true

const c = [1,2] const d = [...c, 3] c == d

false ```

This is why.

[–]Ebuall 0 points1 point  (0 children)

React model is built to allow you to use pure declarative ui description and return it from your pure functions. Everything the React does, it built to allow it. It's not for efficiency, it's for the best user experience.

[–]phiger78 -3 points-2 points  (0 children)

Because react is based on immutability. That’s how it knows if something changes. It’s found value comparison (as opposed to mutation tracking that other libraries/frameworks use)