[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)