28 ms·
Why React Re-Renders
- Bolkan 4y agoJS works in mysterious ways.
- codingdave 4y agoI think that is the point, is that it does not. It is code, and it does what it is told. But it is quite possible to be a professional dev without fully understanding exactly how and why your frameworks doing their thing, so it feels hand-wavy and mysterious.
- 4pkjai 4y agoI'm among the React developers who don't really know how it works under the hood. Recently I needed to code a UI that required mouse drag events. It ran like a pig when I did it in React. I tried a few things to speed it up, but eventually gave up and did the UI in plain old Javascript. It runs a hell of a lot better, but the code does feel a lot more flimsy. Edit: Here's a demo of the UI I built https://www.youtube.com/watch?v=Wt71bNYe3qc https://www.youtube.com/watch?v=Wt71bNYe3qc
- spoils19 4y agoI'm no expert, but mouse drag events in React are fairly simple to get working performantly, even without understanding how it works under the hood. There are even many libraries that provide functionality, all without it running 'like a pig'.
- lioeters 4y agoAlso: > Can pigs run fast? Domestic pigs can run as fast as 17 km/h while wild pigs can reach a speed of 30 km/h!
- 4pkjai 4y agoYes, but I didn't want to use a library because I was doing something a bit out of the usual case. I do like understanding my code as much as possible. So I was choosing between: 1. Understand React better, and reading about the "React" way to use these mouse events. 2. Doing it in VanillaJS
- ramesh31 4y ago>I do like understanding my code as much as possible. So I was choosing between: 1. Understand React better, and reading about the "React" way to use these mouse events. 2. Doing it in VanillaJS The great thing about the "React" way of doing things is that it's just the JavaScript way of doing things. React can be summed up entirely as: "a function that takes in props and returns rendered HTML". It is not a framework. There is no black magic. There are no idioms. There are no batteries included. Anything else you do with it beyond that is entirely up to you.
- spoils19 4y agoMy point was not to use a library, but that many others have implemented functionality in libraries without performance decreases. Granted, your use case may be special to the point where vanilla JS is better, but given how many libraries are out there, as well as how many may simply be poorly implemented yet still work fast, makes me wonder what you were indeed doing.
- IceDane 4y agoThere is nothing about react that requires you to understand "how it works under the hood" to use mouse events in react. You just need to sit down and read the tutorials and learn to use react properly. Try the new beta docs.
- 4pkjai 4y agoTrue, but I'm quite happy using VanillaJS. I'd like to avoid React as much as possible in the future.
- isbvhodnvemrwvn 4y agoIt's not unlikely that you missed some hints of the latter part of the article (JS equality for functions or objects) although it's difficult to say without seeing the code.
- cantSpellSober 4y agoDraggable UI (w/o libraries) is one of the places I enjoy React most, maybe re-rendering wasn't to blame. Might have been an issue under the hood (as drag fires multiple times a second)? From the article: > I think as developers, we tend to overestimate how expensive re-renders are. In the case of our [pure] component, re-renders are lightning quick. > If a component has a bunch of props and not a lot of descendants, it can actually be slower to check if any of the props have changed compared to re-rendering the component.
- IceDane 4y agoYou seem to be conflating javascript and react here, and even if you weren't, the entire point of this article is that nothing works in mysterious ways.
- synu 4y agoIf you don’t mind a slight tangent, where would you start today to learn front end development with React such that you learn this sort of thing as you go?
- azangru 4y agoIf you are starting to learn front-end development today, you may question the choice of React. Consider that a decade ago, people who were starting with the frontend were learning jQuery, which is almost irrelevant now.
- ramesh31 4y ago>Consider that a decade ago, people who were starting with the frontend were learning jQuery, which is almost irrelevant now. It's not that simple. At the time, jQuery was absolutely pivotal in bringing about ES5 and the transpilation revolution on the front end. More or less its' entire API was subsumed by the browsers, and so jQuery became unnecessary, but far from irrelevant. I can see the same thing happening today with React/JSX. There is simply no better way of expressing a UI than JSX-like components. And FRP as a paradigm for UI development is here to stay. So the future of web dev probably looks something like Yew [0]. [0] https://yew.rs/docs/getting-started/build-a-sample-app https://yew.rs/docs/getting-started/build-a-sample-app
- outworlder 4y agoWhat would they be using instead?
- azangru 4y agoThey should probably start with native browser apis for DOM manipulation, and web components. And then explore the current landscape for options that alleviate the pain points discovered while learning (e.g. Lit is nice for declarative reactive components). All the while being conscious of the tradeoffs.
- 4y ago
- PeterWhittaker 4y agoI sometimes wonder what people are using React for, that they wouldn’t know this or have figured it out along the way. Let me kind of explain, as best I can, without code. We have a complex software product, an integrated compliance and risk management system with embedded workflow, automatic highlighting of potential risks due to non-compliance, plans of actions (aka risk management plans), RBAC, ABAC (used to control different things), etc. The backend does most of the work. What gets presented at the frontend can be complex. The React component model gives us an almost functional DSL that we use for compact, highly expressive tooling that allows us to integrate common look and feel, table exports, etc., across some really complex data. We started before Hooks were widely available or widely known, and, we started with class-based components, because we realized pretty quickly we were going to have to do some funky state management, especially where workflow was involved (we want a common look and feel across processes and tasks, so workflow tasks and processes get wrapped in higher level components that call back to other high level components; the forms can have dozens or hundreds of variables, some interdependent, so state has to be communicated up for validation; React handles sending it down). Our key state management function is a custom signal handler that gets passed as a prop to all sub components, then, via a common wrapper, back up to the high level components that group everything for display and consistency purposes. As we refined this handler (and a few others), we moved from class-based to functional components. The functional components are simpler than the class-based components they replaced, with much of the complexity located in one place, the handler. Our handler allows us to pass state up “just far enough”, and React handles updating all affected components. Performance is fantastic, validation is easy(ish; it takes some staring to grok how it hangs together), and debugging (once one has grokked, is straightforward(ish; there are edge cases that cause pause, until the “oh, yeah, that” moment). That functional DSL has allowed us to build several suited-for-purpose DSLs, e.g., our workflow system, which, while not no code, is low code (configuration as code more than anything else). If we didn’t understand the React state management model, none of this would have been possible. So I ask, in naïveté, what are people building that they didn’t need to know that? (We are likely to move to Hooks anytime soon, because we’ve already solved the problem they were introduced for, and without having to rewrite much. Hooks look like we would have to make wholesale changes, in which we see little value, at least right now, OMMV in the future).
- acemarke 4y agoJosh's post is excellent! Related, a couple years back I wrote a post on the same topic: "A (Mostly) Complete Guide to React Rendering Behavior" [0]. It's longer and has more details, but fewer diagrams :) (Josh's ability to make interactive posts is amazing.) I originally wrote my "Rendering Behavior" post specifically because the existing React docs didn't clearly spell out this kind of behavior, and I was _constantly_ seeing questions that showed people didn't understand these concepts well. For example, many folks assume that "React re-renders my component when its props change", when in fact the real answer is that "React re-renders recursively by default, _regardless_ of whether or not any props changed". There's also a lot of confusion over how Context ends up affecting renders, and I've seen folks end up in situations where setting state in an "context provider" parent component ends up rendering the _entire_ app - not because Context changed, but because they did a `setState()` and that's the natural behavior. So, I was trying to help clarify those sorts of nuances. I can say that it's been one of the top couple posts I've written in terms of traffic and positive feedback (along with my "Redux vs Context differences" post). The good news is that the new React beta docs [1] _do_ cover at least some of this rendering info, although it's more contextual in various points of the explanations and in less detail. I'm hopeful that after the main tutorial/API reference material is done and the docs are opened up for external contributions, that we can help add a couple pages that cover some of this rendering behavior info as well. There's a few other good articles I've also seen covering this topic as well [2] [3] [4]. [0] https://blog.isquaredsoftware.com/2020/05/blogged-answers-a-mostly-complete-guide-to-react-rendering-behavior/ https://blog.isquaredsoftware.com/2020/05/blogged-answers-a-... [1] https://beta.reactjs.org https://beta.reactjs.org [2] https://www.zhenghao.io/posts/react-rerender https://www.zhenghao.io/posts/react-rerender [3] https://alexsidorenko.com/blog/react-render-cheat-sheet/ https://alexsidorenko.com/blog/react-render-cheat-sheet/ [4] https://www.developerway.com/posts/react-re-renders-guide https://www.developerway.com/posts/react-re-renders-guide
- westoncb 4y agoI've been looking for something like your rendering behavior article :) One question though: is it pretty up to date with changes since 2020? (I think there were some things in React 18 that would affect rendering behavior—not sure though).
- fabian2k 4y agoThe big part that seems to thoroughly confuses developers new to React is the difference between rendering and reconciliation. This is not particularly difficult to understand, but the original emphasis on the virtual DOM seems to lead to a misunderstanding on how React works. The vDOM plays a role only after rendering happened, so all that diffing stuff doesn't have anything to do with rendering. To me that seems like one of the primary causes of developers being surprised that React rerenders more than they expect. I like the explanation on what triggers rendering in this post. Props and state are usually used in the explanation for this part, but props actually only matter if you want to use React.Memo. And the one part every React developer should know is that in the absence of React.Memo all children will rerender if any state changes.
- DanielHB 4y agoyeah, this is key. What you should be REALLY avoiding is unnecessary DOM updates, rerenders have a cost too, but marginal compared to DOM updates and even then you mostly don't need to care either except in extreme situations (like useCallback is not really necessary most of the time0
- srk_hn 4y agoCan someone tell me why this page needs 13MB of resources to display?
- fabian2k 4y agoIt's even more for me, it looks to me like the embedded React sandboxes are essentially full React apps in development mode. So none of the usual optimizations for size are active, and you get the full devel-mode bundle for several React applications on this page.
- MH15 4y agoMy guess is for the code editors and associated transpiler. The demos allow you to edit the code in JSX, requiring the tooling. Kinda impressive imo.
- quickthrower2 4y agoThis page doesn't know if it is an app or blog post then! Maybe it should lazy load those resources?
- neon_electro 4y agoHere are the top requests by file size from the page, screenshotted since I couldn't easily copy/paste: https://imgur.com/N1ANEIb https://imgur.com/N1ANEIb Would love to better understand why the site is pulling in the Babel transpiler in production? Edit: siblings know more than me, thank you siblings!
- rajangdavis 4y agoWhen I checked it was 46MB! Looking at the network tab, it seems to be making a bunch of extra requests for the same assets. I know that Vercel/Next.js will lazily load in certain JS chunks but there seems to be some mechanism that is loading a ton of stuff up front. Edit: from other comments in this thread, it's the sandbox code, really crazy how much overhead that adds to the page!
- AtlasBarfed 4y agoNow that is how you start a flame war in the most subtle way possible.
- gregsadetsky 4y agoJosh's content is always very very high quality. I look forward to him releasing his online React course [0] so that I can recommend it to others who are starting out. My personal biggest not-total-comprehension is around Hooks / effects. I've followed tutorials, used them in production, etc. I'm comfortable using them but I also consider them a bit of a black box, which I don't like (e.g. I'm not sure how they're implemented). The other (even bigger) question for me is "why" -- what prompted the React team to change everything over to "effects". Anyone? [0] https://www.joyofreact.com/ https://www.joyofreact.com/
- fabian2k 4y agoYou can reuse hooks between multiple components. You can't really do that with the lifecycle methods of class-based components.
- DanielHB 4y agoyou can with mixins doesn't mean you should...
- vanjajaja1 4y agoDan had a good summary of why hooks happened which I can't find now, the tldr was that there was a bunch of bugs introduced around referenced props/state being stale and unpredictable in components. Hooks was the natural evolution that removed all possibility of accidentally referencing stale data.
- joshribakoff 4y ago> Hooks was the natural evolution that removed all possibility of accidentally referencing stale data. It unfortunately does not, you can hve various foot guns with refs or omitting dependencies from the array argument; but to your point it makes it a lot easier to ensure you do it correctly by collocating logic related to each cross cutting concern instead of entangling the code in the lifecycle methods. More precisely it makes it easier to follow correct patterns, but as always that is subjective and hooks can be contentious :)
- scyzoryk_xyz 4y agoI need to finally buy his CSS course - all the details in his pages and materials are just gorgeous.
- kareemsabri 4y agoGood article. What I've found interesting is how many developers think a React "component" (which since hooks is just a function) has some special privileges or abilities that a normal JS function does not. Like, whether it will be selectively executed or what variables are created anew versus reused between subsequent calls. It seems unclear that a React component is just a function, and displays all the behavior expected in a plain old function. While I agree it was hard to know when React would re-render in the old, class component paradigm, it seems much easier to know when a function will re-render, since it has to re-render whenever the function is called.
- jasonkillian 4y agoIt is a bit more complicated in practice though than "a React component is just a function that rerenders when called". In some ways, the function acts more like a class, and then React, internally, uses it to create "instances" of components that have their own set of data stored. (Which is why hooks like useState, useRef, etc. can work - because data is being stored internally in React tied to a component instance.) It _is_ true that when you call a React function component it "runs its code" just like any regular old JS function. But when that function gets run and what all the side effects of its code are actually is quite complex.
- kareemsabri 4y agoYeah, this is not to belittle the complexity of React under the hood. But they are functions, and it seems you can assume they will be called in a straightforward manner when they render (whether they are invoked as explicit function calls or via returned JSX). The only real complexity (for the developer) is the use of hooks, effects etc. if you don't mess with useMemo (which you generally shouldn't). Certainly they aren't pure functions, they have side effects and are stateful, and that has some nuances, but (kudos to the React team) once you understand hooks as a reference to the instance value and a setter for that value, they're pretty easy to understand. I guess I don't personally find thinking of them as a class as that useful, my mental model of "it's just a function with some external references (via hooks)" gets me there.
- petilon 4y agoA pain point with React is large data structures. To re-render (assuming class components), you can setState with the changed property. For example if your state has two properties named foo and bar, and bar has changed then you call setState({ bar: newValue }). This works if you have simple properties. What if you have large complex data structures, and you need to modify a property deep down inside? Then you can make a copy of the data structure, then modify the field in the copy, then call setState({ bar: copyOfLargeObject }). But it is tremendously wasteful to make a complete copy of a large data structure! A workaround is to not make copies of large data structures. Just modify the large object directly. Then just call setState({}); That's right... setState() with an empty object triggers a re-render. Now you don't even have to store the object being modified in state. You can hold the large object in a member field of the class. So even though your component is stateful, you are not telling React what your state fields are - you are managing it yourself. At this point, React's programming model has broken down.
- acemarke 4y agoFWIW, React has always been designed around some Functional Programming type principles, such as immutable updates. Immutable updates do in fact require that if you want to update `state.some.nested.field`, you have to make copies of _all_ objects in that path: `nested`, `some`, and `state`. This isn't unique to React. Yes, class component `this.setState()` lets you get away with mutations. That's not really a _good_ thing. If anything, it's a holdover from an earlier era of JS, where it was much more common to use React with data structures that might be mutable. With the `useReducer/useState` hooks, the React team explicitly designed them to require immutable updates and pass in new references, otherwise they'll bail out, assuming that since it's the same reference nothing was changed and no render is needed. Some more details: - https://blog.isquaredsoftware.com/2020/05/blogged-answers-a-mostly-complete-guide-to-react-rendering-behavior/#immutability-and-rerendering https://blog.isquaredsoftware.com/2020/05/blogged-answers-a-... - https://beta.reactjs.org/learn/updating-objects-in-state https://beta.reactjs.org/learn/updating-objects-in-state
- AgentME 4y agoWhen updating deeply-nested immutable objects, the Immer library is great. You call the `produce(immutableValue, draft => { ... })` function with some immutable value and a callback function that manipulates a mutable proxy object mimicking the immutable value, and then the function returns a new immutable value with the same changes made by the callback function. The mutable proxy object never escapes the callback, so the use of Immer stays self-contained as an implementation detail of your code without infecting your component's public API or anything. Another alternative is the "immutability-helper" library, which was originally published by the React team as "react-addons-update". It's a lighter library with less proxy magic going on, but at the cost of being more explicit: instead you create an object describing how to update an immutable value to produce a new immutable value. I've used it in the past in a few places but mostly recommend Immer over it now.
- Rapzid 4y agoBecause hooks 67,48,47, and 51 changed. Edit: Sorry, because the parent component re-rendered too. Except it didn't. But maybe? Nah, it didn't. Did it? A lot of people are going to think this is being ridiculous, but there were actually bugs as recently as a few months ago(maybe still?) in the dev tools where they would not know the actual re-render reason and it would spit out a generic "parent component re-rendered" instead.
- deleted 4y ago[deleted]
- robertwt7 4y agoThe blog is sooo good! I enjoyed every single paragraph when reading it. If you're confused about the last part where it mentioned where useMemo and useCallback can help when passing props, I completely understand after reading Kent C Dodds block and Dan Abramov. Further, I learned a lot by taking interactive course from Dan Abramov in https://justjavascript.com https://justjavascript.com Highly recommended! good read!! https://kentcdodds.com/blog/usememo-and-usecallback https://kentcdodds.com/blog/usememo-and-usecallback https://overreacted.io/a-complete-guide-to-useeffect/ https://overreacted.io/a-complete-guide-to-useeffect/
- francislavoie 4y agoTo follow up on that, the new beta react docs are largely written by Dan, and they explain the concepts very well, more formally. IMO they're a must-read, even if they're not quite final (some sections missing because they're doing each part carefully one at a time). They're a great refresher and/or confirmation that you're "doing it right", and there's many lessons to be brought into how you approach writing and reviewing components.
- emptysea 4y agofrom the article: > If a component has a bunch of props and not a lot of descendants, it can actually be slower to check if any of the props have changed compared to re-rendering the component. (I don't have a source for this claim, but I've seen prominent developers like Dan Abramov make this case on Twitter) It might vary from application to application but this seems like something that could be tested. Also on the other side of the coin, you have memo all the things: https://attardi.org/why-we-memo-all-the-things/ https://attardi.org/why-we-memo-all-the-things/
- solardev 4y agoThis was fantastically written! Thank you.
- tobyhinloopen 4y agoReact would have been great in a functional language with immediate data structures. Instead it’s used with JavaScript, where you have to constantly force yourself to create your prop objects in a very specific way to avoid or force (re)rendering. It’s a constant struggle to control re-rendering. I still can’t believe React became as big as it is. Vue and Angular just fit JavaScript better.
- fulafel 4y agoIt's very widely used in the ClojureScript world (via Reagent) where it indeed has a better impedance match.
- geokon 4y agoAnd sort of in Clojure with cljfx (React-like on JavaFX) https://github.com/cljfx/cljfx https://github.com/cljfx/cljfx
- tshaddox 4y agoIt’s quite nice in ReScript as well: https://rescript-lang.org/docs/react/latest/elements-and-jsx https://rescript-lang.org/docs/react/latest/elements-and-jsx
- mlajtos 4y ago> immediate data structures Immutable? But React in JS is a blessing – it brought good thinking to messy world. And as a direct influence, JS will adopt immutable data structures – tuples and records. https://github.com/tc39/proposal-record-tuple https://github.com/tc39/proposal-record-tuple
- Existenceblinks 4y agoMy brain reads the title as "why not calling `render()` manually?". Probably unpopular opinion: manually call `render` is always better. Creating your own `ui = f(state)` is very trivial with DOM event, custom element, and a whatever 5kb pure view lib. With event delegation technique and a global store, I can build almost whatever ui with this pattern.
- valenterry 4y agoIt's both. You should be able to control it, but can have a same default that does it for you automatically under certain conditions. Unfortunately React forces the latter only
- Existenceblinks 4y agoYes, practically force re-render comes with unnecessary mechanism to prevent it from happening, and there are unimaginable cases of state mutation that shouldn't force re-render. Example situation: changing state A, B and C, re-rendering, changing B again, not rendering. Auto-render + prevention mechanism is inferior means compared to just having `render` and just calling it whenever and wherever.
- AtNightWeCode 4y agoThe opposite of Big Misconception #1 is also common. People who think React never re-render anything besides very specific areas that needs to be updated.
- endless90 4y ago> I know some developers believe that every state change in React forces an application-wide render, but this isn't true. Re-renders only affect the component that owns the state + its descendants (if any). The App component, in this example, doesn't have to re-render when the count state variable changes. Does that mean if i have a redux store attached to my app it rerenders everything when i change something small in the store? Because the store is usually one of the top most HOCs.
- HarmonicTons 4y agoNo. The redux <Provider> component is just a simple React context that uses `children` [1]: <Context.Provider value={contextValue}>{children}</Context.Provider> And `children` behave diferently. `children` is a prop and thus not considered as an element of the Provider component, but of the App component [2]. Meaning the components inside `children` will rerender when App renders, and not when Provider renders. 1: https://github.com/reduxjs/react-redux/blob/master/src/components/Provider.tsx#L61 https://github.com/reduxjs/react-redux/blob/master/src/compo... 2: https://www.developerway.com/posts/react-elements-children-parents https://www.developerway.com/posts/react-elements-children-p...
- Zealotux 4y agoNo, there is optimization happening at Redux level to ensure components only gets updated when they need to, but it's up to you to ensure your store is properly designed and consumed to avoid unnecessary updates. Look up Mark Erikson's blog[1], who's a Redux maintainer, for a lot of details on Redux's internals (acemarke on HN), he also answers tons of questions everywhere[2] about Redux, he helped me so much understand it! [1] https://blog.isquaredsoftware.com/ https://blog.isquaredsoftware.com/ [2] https://stackoverflow.com/a/40386189 https://stackoverflow.com/a/40386189
- acemarke 4y agoThanks, glad to hear that info's been helpful! :)
- acemarke 4y ago
- Const-me 4y agoDon’t browsers already have similar update logic implemented in their HTML DOM? If the DOM tree stays but a property of some element changes, if that property affects size or layout or other visual state of things, the browser will re-render the changed element, along with any dependent elements. Could be many elements when re-layout is needed, could be just one if the only change is color.
- gherkinnn 4y agoBefore you memo, give Before You Memo() [0] a read. Building on Josh's bit about the UI tree (It's not about the props) [1], just separate the counter and the decoration in to separate sub trees. Doing so leads to nicer component structures anyway. [0] - https://overreacted.io/before-you-memo https://overreacted.io/before-you-memo [1] - https://www.joshwcomeau.com/react/why-react-re-renders/#its-not-about-the-props https://www.joshwcomeau.com/react/why-react-re-renders/#its-...
- DanielHB 4y agoOne funny thing about React is that the most dangerous kind of developer is the one with middle-level knowledge of it beginners don't know enough to do optimisation seniors know enough that they only optimise what is needed mid-level are capable of build code that needs to be optimised, but they tend to over-optimise and often do the optimisation wrong for example what a mid-level might do: const C1 = React.memo(({values}) => {...}) const C2 = () => { return <C1 values={[1,2,3]} /> } That React.memo is useless because arrays and objects are mutable in JS It also reveals a flaw in JS/React in that memoization often needs to happen at the parent instead of the child, creating awkward code and implicit behaviour and implicit performance traps when forgetting to memoize object/array props that the child expect to be memoized. The child decides which props should be memoized, but doesn't enforce it and no amount of typescript will catch that problem Fortunately there is a proposal that will fix this problem by introducing records and tuples like python (immutable objects/arrays) https://github.com/tc39/proposal-record-tuple https://github.com/tc39/proposal-record-tuple but it still seems far off to being usable as I don't think it will be viable to polyfill it for browsers that don't support it