[HN Gopher] Why React Re-Renders ___________________________________________________________________ Why React Re-Renders Author : clessg Score : 136 points Date : 2022-08-16 16:50 UTC (6 hours ago) (HTM) web link (www.joshwcomeau.com) (TXT) w3m dump (www.joshwcomeau.com) | synu wrote: | If 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? | jasim wrote: | This is diffused cultural knowledge, but most of the ideas in | React can be understood as: view = render(state). It re-renders | everything all the time. But practical considerations force | some optimizations. Everything else in React follows from those | optimizations. | | For example, if you destroy and re-create DOM all the time, | then it loses critical information like user's cursor position | and text selections. It is also slow to read and write to the | DOM. Thus the need for an intermediate data structure, the | virtual DOM. | | React re-renders the virtual DOM every time the state changes. | And it then diffs the previous and current ones against each | other and does just the minimal number of DOM mutations to sync | it up. To speed this up a bit, array elements are denoted with | "key" so (I assume) there is a way to see if an element has | been added or deleted. | | But re-rendering the virtual DOM all the time can also be | costly in terms of performance. Thus the next set of | optimizations: React.memo+immutable data, and so on.. | lhnz wrote: | The new version of the official docs is currently in Beta and | this is really, really good: https://beta.reactjs.org/learn | | (In fact, I'll go further than this and add that the section on | "Escape Hatches" should be re-read by senior/lead engineers, as | many have misconceptions due to learning concepts ad-hoc from | code of mixed quality: https://beta.reactjs.org/learn/escape- | hatches) | gregsadetsky wrote: | Scrimba has pretty great interactive classes too. They | specifically have a free React one: | https://scrimba.com/allcourses?topic=react | | It's quite high quality and the learning environment is great: | you're in a live code editor + hear and see the teacher's | code/cursor movements. | azangru wrote: | If 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 wrote: | >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 | outworlder wrote: | What would they be using instead? | azangru wrote: | They 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. | gherkinnn wrote: | It does help to have used the basics to understand the | benefits of a full framework. But learning things bottom- | up isn't suitable for everybody. | | As for web components, I'd rather not. Nothing about it | works for me. Not the class-based decorator syntax. Not | the CSS scoping. Not the use of custom elements. The prop | syntax is awful and the paradigm just feels cumbersome. | | I work with Angular (its component model is close enough) | and have read the Lit docs. Never will I choose that | option. | | A function is just a better way to write UIs. | 411111111111111 wrote: | That's a fine idea if you're not expecting the team to | grow beyond the original author(s). | | It's a pretty terrible idea if you're going to do it in a | business setting, as the original author(s) will always | be it's Achilles tendon, making the project a liability | before it even goes into production. | | Most projects use react or angular because it makes | onboarding new members easier, and these frameworks | really aren't as bad as some people on hn claim. | azangru wrote: | How much have you researched this space? Do you know of | companies that are successfully building their products | with web components? Hint: these would include Adobe, | Microsoft, RedHat, and GitHub. How do they onboard new | members to their terrible setup, one might wonder? | RussianCow wrote: | > How do they onboard new members to their terrible | setup, one might wonder? | | I can't speak for the specific companies you listed, but | the number of times I've heard someone sing praises about | a homegrown UI framework at their place of employment is | approximately zero. The sentiment expressed about those | is generally hatred and agony. | | That's not to say it _can 't_ be done well, but most | don't. Also, a company being able to hire/onboard | engineers does not imply that their onboarding process is | smooth, or that their house-made framework is well | designed. | azangru wrote: | > a homegrown UI framework | | I am deeply puzzled by both your and the sibling comment, | which suggest that the only way to go is to build a | framework. To advance such argument, especially when | comparing something to React, is to forget that: | - React also for a long time was advertised as a view- | layer library for creating UI components, not as a | "framework". - There've been numerous debates in | which advocates of Angular or Ember were suggesting that | because of such inherent lack of structure, React apps | were always different between projects, as opposed to the | clear conventions used in Angular or Ember project. This | did not deter React supporters and did not prevent React | from succeeding. - React was created as a library | when web browsers did not have a standardized component | model; just as jQuery was created as a library when | browsers did not have a unified way of interacting with | the DOM. Years have passed, and web browsers have matured | to the point when a native component model has become a | reality. You do not need to home-grow a framework in | order to take advantage of them. | ReadTheLicense wrote: | Problem is, Web Components still don't support reactivity | and passing complex props, so you need a framework | anyways, and at that point it might as well be React. | | I've seen people do abominations like each web component | is a React root, message passing systems on the side for | complex objects... better use React directly | nwienert wrote: | Was going to say the same as sibling @ReadTheLicense, but | further Web Components were started _before_ React so the | idea that they are more modern isn 't true. | 411111111111111 wrote: | Sure, if that's the scale of your project right from the | start then creating a new framework from scratch is | always an option. | | That's the origin of both react(Facebook) and | angular(google) after all. | cercatrova wrote: | The phrase is Achilles' heel for which the tendon is | named. | nfRfqX5n wrote: | If you want a job, learn react | ng12 wrote: | The idea is you shouldn't really need to. You have tools to | memoize expensive operations in a React-friendly way, but even | then you shouldn't really have to think about render cycles. | | YMMV but I the only time I've ever had to really think about | this stuff was when trying to frankenstein legacy jQuery code | into a React app. | RussianCow wrote: | In my experience, it's pretty easy to hit performance | bottlenecks in React, so I don't think it's that uncommon to | have to dive deeper for any reasonably complex codebase. | Also, you basically _have_ to understand the React render | cycle to effectively use `useEffect` for anything more | complex than "do this thing on mount". | RyanHamilton wrote: | I can recommend React with Mosh: | https://codewithmosh.com/p/mastering-react I completed it and | together with the react official documentation (particularly on | hooks and newer react 18 features) have learnt enough to build | a good interactive application. Building the app immediately | after has taught me much more. You can try patching free | tutorials together but given the salary paid to good | developers, paying for good training is a great investment. | | I chose react as I had a large application to make and I knew | react had 2 critical libraries I wanted to reuse. Having now | learnt react and the underlying ideas, I think Svelte | (https://svelte.dev/) may solve the general problem better. | However it has less libraries/documentation and community | support. If I had more time to learn I would have considered | learning it as a potentially superior solution. | Tiktaalik wrote: | I've found myself needing to learn React on the go while | working on an evolving React codebase, and needing to improve | performance significantly and it's been _very hard_ because | casually googling for how to do things in React yields lousy | hits full of extremely high level "babys first react todo | app/blog" type articles. | | Absolutely not what I need. | | What I want to know is how to build things with React as | performant as possible. This discussion of complex and | performant apps seems elusive. | [deleted] | [deleted] | kareemsabri wrote: | Good 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 wrote: | It 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 wrote: | Yeah, 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. | leeoniya wrote: | > if you don't mess with useMemo (which you generally | shouldn't) | | why? is this not the primary way to re-init expensive | internal component state when specific props change? | hither_shores wrote: | GP's claim is a little too strong, but in my experience | most uses of `useMemo` / `useCallback` are only necessary | because people define things in the wrong scope, or write | giant spaghetti components with 15 different props and no | internal structure. The best memoization technique is not | calling things repeatedly in the first place. | kareemsabri wrote: | He touches on this in the piece briefly. | | > I think as developers, we tend to overestimate how | expensive re-renders are. In the case of our Decoration | component, re-renders are lightning quick. | | Certainly it's there for a reason, and you may have | expensive operations, but in my experience developers | reach for useMemo much too early and often, and it just | adds complexity to their functions. The cost of checking | the parameters for changes adds overhead that may be more | expensive than just re-doing the "expensive" operation. | My rule of thumb is if the operation is less than O(n) | where n < ~5000 I don't reach for useMemo. | | There have been some benchmarks done on this, and when it | pays off to use. | petilon wrote: | A 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 wrote: | FWIW, 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-... | | - https://beta.reactjs.org/learn/updating-objects-in-state | AgentME wrote: | When 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. | hither_shores wrote: | > But it is tremendously wasteful to make a complete copy of a | large data structure! | | You don't make a complete copy: you make a shallow copy of the | spine, and then only descend along the fields you actually | modify. The number of operations scales as O(branching factor * | depth), not O(size). | srk_hn wrote: | Can someone tell me why this page needs 13MB of resources to | display? | MH15 wrote: | My 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. | neon_electro wrote: | Here are the top requests by file size from the page, | screenshotted since I couldn't easily copy/paste: | 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 wrote: | When 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 wrote: | Now that is how you start a flame war in the most subtle way | possible. | fabian2k wrote: | It'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. | PeterWhittaker wrote: | I 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 naivete, 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). | gervwyk wrote: | Sound pretty interesting! We've also implemented a DSL on top | of React, see Lowdefy [0] | | We've taken a different approach, we've written a pure js | engine which computes and manages state based on operator used | to express logic, and then have a recursive render loop in | react which provives engine with update hooks to it uses to | rerender components when it should. That way we can very handle | complex state logic and then update with ease all without | dealing with passing state up and down. | | We still have a few ideas on how to further optimize which will | be built in future versions. But already we, and the OS | community are building some advanced apps using Lowdefy | | [0] - https://github.com/lowdefy/lowdefy | willio58 wrote: | You can get away with a lack of understanding about these | things and still create beautiful products. For an example just | look at Josh's website. Beautiful react and design execution | without knowing these details about rerendering. | tshaddox wrote: | > I sometimes wonder what people are using React for, that they | wouldn't know this or have figured it out along the way. | | The article is for beginners (the article itself says its | intended audience beginner-intermediate, but I'd say it leans | very much towards beginner). Thus this is precisely an example | of how people who use React would learn this. It would be odd | to comment on every explanation of a concept that everyone | would have already learned that concept. | fabian2k wrote: | The 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. | Bolkan wrote: | JS works in mysterious ways. | codingdave wrote: | I 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 wrote: | I'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 | isbvhodnvemrwvn wrote: | It'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. | IceDane wrote: | There 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 wrote: | True, but I'm quite happy using VanillaJS. I'd like to | avoid React as much as possible in the future. | cantSpellSober wrote: | Draggable 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. | spoils19 wrote: | I'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 wrote: | Also: | | > 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 wrote: | Yes, 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 | spoils19 wrote: | My 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. | ramesh31 wrote: | >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. | recursive wrote: | "Rules of hooks" is a thing. | westoncb wrote: | That's a bit too strong imo. There is magic, and it can | generally be found in implicit re-render conditions. | IceDane wrote: | You 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. | gregsadetsky wrote: | Josh'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/ | HeyImAlex wrote: | Hooks compose, whereas side effects and memoized values | sprinkled through component constructors and lifecycle methods | do not. | | For example, the equivalent of useEffect required calls inside | of componentWillMount, componentDidUpdate, and | componentWillUnmount. You try and make something like this re- | usable and you'll be leaking details of your implementation | across the whole component via inclusion in these lifecycle | methods, not to mention any data you're shoving onto the | component instance. But it's still doable. | | Now, what if you wanted to use this re-usable behavior inside | of another re-usable behavior? It gets complicated fast! Now | your library needs to expose the lifecycle-updating methods of | the underlying library, leaking details all the way down. Hooks | are opaque from the perspective of lifecycle, while still | having access to all the same... hooks. | vanjajaja1 wrote: | Dan 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 wrote: | > 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 | :) | fabian2k wrote: | You can reuse hooks between multiple components. You can't | really do that with the lifecycle methods of class-based | components. | knodi123 wrote: | Hooks feel like a good system that stopped right before it | became pretty. Like, an componentDidMount event is a now | useEffect with no second argument? But a componentWillUnmount | event is now a useEffect, with no second argument, that returns | a callback? | | It's powerful, and it's not that hard to use, but it's cryptic | and random. | | Why call it useEffect instead of some other more meaningful | phrase? I mean, "componentDidMount" tells you _exactly_ what it | is. Does "useEffect"? | | Why should onload things be a function, but onunload should be | a function returned by a function? | | Why does useRef give you a thing used for attaching handles to | DOM elements so you can refer to them elsewhere, AND it gives | you an object whose .current property can be used as a variable | that persists without causing a render? | | It's like someone complained that there were two many functions | in the API, and their names were too long, so they | overcorrected in the opposite direction. | madeofpalk wrote: | > Like, an componentDidMount event is a now useEffect with no | second argument? | | Doesn't useEffect with no second argument run after _every_ | render? componentDidMount 's equivalent is for an empty | dependency array in the second argument? | ajkjk wrote: | I love hooks, but I think they made a few mistakes. The | weirdness around useEffect having different behavior with no | second argument vs [] is one of them. | | And they should have included a useUnloadEffect() by default, | even though it's trivial to write, just for clarity. It's way | too easy to miscount the number of ()s in useEffect(() => () | => {}, []); | | They also should have included a few other basic hooks, like | useStableValue() for just computing a constant once on first | render. | | I hate the name `componentDidMount` though. useEffect seems | much better to me. | AgentME wrote: | It seems like almost always if you want to do anything | during unmount, it's cleaning up something you set up in a | useEffect call, which you also want to do any time the | useEffect call's dependency list changes, so it being part | of the useEffect hook helps programmers fall into the pit | of success. React's older class-based components had the | unmount handling separate (componentWillUnmount), and in my | experience on a large React codebase, many if not most | components using componentWillUnmount were subtly flawed | and failed to handle certain cases where props or state | updated in a way that should have caused effects to be | cleaned up or adjusted. React's useEffect hook way of | grouping up effects with their own cleanup code helps | people handle these cases correctly even without realizing | it sometimes. | kareemsabri wrote: | useRef is useStableValue | nickdandakis wrote: | You're translating the class-based way of working in React | against hooks, without stopping to consider understanding | hooks as a first-class principle instead of a translation. | | It's called useEffect because it runs on the (side)effects | observed by the dependency array. An empty array happens to | happen on mount. useEffect returns a teardown function which | happens to correlate with unmount when an empty array is | passed. | | It's called useRef because it returns a (mutable) reference | that's detached from the reactive layer. The DOM element | connection is just sugar. | | I agree that useEffect has a lot of footguns, but this | position seems very shallow. The whole pattern changed, it | doesn't make a lot of sense to continue comparing the two | ajkjk wrote: | > e.g. I'm not sure how they're implemented | | It might help to have a simple mental model for them? | | Picture there is a global variable called _currentComponent: | _currentComponent: { previousValue: React.Element | hooks: HookValue[] currentHook: number | firstRender: boolean } | | Each HookValue is whatever that hook wants. For useState | something like: HookValue = { hookName: | 'useState', value: [state, setState], } | | Before your component is run, the renderer sets | `_currentComponent` to your component with its hooks set to | their current values. | | If you run `useState(initial)` it looks like this: | function useState(initialValue) { const component = | _currentComponent let hookValue; if | (firstRender) { hookValue = { hookName: | 'useState', value: [initialValue, (newValue) => { | hookValue[0] = newValue markForUpdate(component); | // some React-provided function to mark this as needing an | update } } | component.hooks[component.currentHook++] = hookValue; | return hookValue.value } else { hookValue = | component.hooks[component.currentHook++] | assert(hookValue.hookName == 'useState') return | hookValue.value } | | Surely not perfect, but totally useable as a model of what's | going on. To be honest I wish React showed pseudocode like this | in the tutorials, it makes it a lot easier for me to | understand. | andyjohnson0 wrote: | Thank you for posting this. | acemarke wrote: | Yep! Also see Shawn Swyx Wang's talk "Getting Closure on | React Hooks", where he goes through an equivalent example in | more detail: | | https://www.swyx.io/hooks/ | acemarke wrote: | Josh'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-... | | [1] https://beta.reactjs.org | | [2] https://www.zhenghao.io/posts/react-rerender | | [3] https://alexsidorenko.com/blog/react-render-cheat-sheet/ | | [4] https://www.developerway.com/posts/react-re-renders-guide | westoncb wrote: | I'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). | acemarke wrote: | Heh, unfortunately "update my rendering post to cover React | 18" has been on my todo list for _months_ now :) | | As in, I literally have a todo list entry that I keep bumping | back until "Next Sunday", because other stuff is higher | priority. (like, trying to get Redux Toolkit 1.9 wrapped up | and shipped... and also playing as much golf as possible | while the weather is decent :) ) | | The shortest answer is that it's still effectively the same - | the one major change is that React 18 will now batch _all_ | updates in _any_ given event loop tick, regardless of whether | those were queued up inside a React event handler or not. | | There's a good discussion on this in the React 18 Working | Group post on "Automatic Batching": | | https://github.com/reactwg/react-18/discussions/21 | westoncb wrote: | Ah okay--no problem. Actually the only concrete rendering- | related thing I'd seen on React 18 was about the auto- | batching, so I'm already clear on the differences there. So | I'll still be giving the article a read--thanks for you | work on it, and enjoy the golf lol. | scyzoryk_xyz wrote: | I need to finally buy his CSS course - all the details in his | pages and materials are just gorgeous. ___________________________________________________________________ (page generated 2022-08-16 23:00 UTC)