Hacker News new | past | comments | ask | show | jobs | submit login
A Visual Guide to React's useEffect (2021) (alexsidorenko.com)
192 points by alonp99 on Dec 26, 2022 | hide | past | favorite | 112 comments



Lots of people seem to misunderstand useEffect. It's not for transforming local state or for handling user events. In fact, there are many many reasons that people use useEffect for where it's not needed, and the recent React beta docs address this [0]. Their categories include:

- Updating state based on props or state

- Caching expensive calculations

- Resetting all state when a prop changes

- Adjusting some state when a prop changes

- Sharing logic between event handlers

- Sending a POST request

- Chains of computations

- Initializing the application

- Notifying parent components about state changes

- Passing data to the parent

- Subscribing to an external store

- Fetching data

Effects should also be idempotent, they should include pure functions that don't have side effects (such as doing a POST request) which Dan Abramov of the core React team addresses [1].

[0] https://beta.reactjs.org/learn/you-might-not-need-an-effect

[1] https://twitter.com/dan_abramov/status/1281669881667162112


A lot of the confusion people have with useEffect is the React team’s dishonest approach to it.

useEffect WAS for side-effects. That’s why it’s named that.

That’s literally the first sentence in the Effects hook page in the current non-Beta documentation.

> The Effect Hook lets you perform side effects in function components:

https://reactjs.org/docs/hooks-effect.html

I completely agree that useEffect should not be used for side effects. The concept of using it to sync with an external system makes far more sense.

But this is a very different and new understanding of what useEffect is. The React team, for whatever reason, does not present it as that. They do not point out that, yes, this was originally supposed to be for effects but they realized that this was a bad idea and have refined how they believe it should be used.

Instead, the documentation and commentary around such changes never point out they are changes and just present them as how things are. Which leads to a tremendous amount of confusion among new developers learning React, because it’s never clear what the actual correct approach is, since you rarely come across commentary in a temporally ordered way.

If the Beta docs included the statement “we used to recommend this for side effects, but now don’t”, any newbie reading that will forever understand why the highly upvoted SO answer from 3 years ago recommends using useEffect for a certain side effect but today, that is probably not the right solution.


Huh? Syncing with an external system IS a side effect; there's no inconsistency in messaging. React considers everything not part of React to be an external system (document, external store, backend...)


The concept of using it to sync with an external system makes far more sense.

React 18 introduced a new hook called useSyncExternalStore for that, which implies useEffect isn't quite right for that purpose otherwise there'd be no need for a new hook.


First time I saw this hook. This seems more like a library implementor’s hook to sync stores from redux, mobx, etc.

You can of course wrap your GET/POST requests to an external network API too but that seems like a lot of unnecessary code to pull in external data.


You never just want to "pull in external data" in a real app though. You need to make the request, deal with injecting auth tokens or headers, handle errors, handle different responses, update global and local state, possibly trigger side effects when the loading starts and ends, do something in a "finally" callback... If your app is non-trivial then you'll almost always end up using a library of some sort. That's where useSyncExternalStore is useful. If you're using useEffect then you're probably not making external requests well.

If you don't need it then you should be questioning whether you even need React, not whether or not you need useEffect.


I do agree that better built-in methods should exist that does most of what people have been using useEffect for. Many frameworks come with many tools for specific use cases but react has always left it to the community. If this or new ones are helpful enough, then I’m up to use them. However, your last statement is a bit too presumptuous to assume that not needing useSyncExternalStore means that someone does not need React. I recommend Remix for most apps now if possible, and the number of times anyone needs to use either hooks discussed is very minimal. Remix uses React.

Think your last reply overlaps with the first half of my message. I reach for swr or similar to manage the lifecycle of client side network requests (or any async requests) if necessary. I would imagine that these libraries or new ones adopt useSyncExternalStore internally. If I want to use this hook myself, it seems quite a bit more work to have to write a subscription-based store just to make certain network requests. Many apps aren’t real time either.

Take a single case: analytics. I want it to trigger whenever the page loads (and can’t be done server side for whatever reason). Are you saying that I need the kitchen sink to make this fire-and-forget request? useEffect is fine here. Am I not making an external request well? I just read the useSyncExternalStore RFC. Its primary purpose is to handle external state change in a concurrent environment. Makes sense. Is it to become the default for all “external requests?” Idk. But let’s agree that not all requests require the same care.


>The concept of using it to sync with an external system makes far more sense.

Why not do that in your setState function?


If you have to document 12 wrong ways to use your API that's a sign that it's not well designed.

React was a breath of fresh air when it first arrived and I'm grateful to it for rekindling my interest in front end javascript but the cracks are starting to show. It's just way too hard to write correct, performant React code. I daily see veteran programmers with years of React experience under their belt surprised and confused by the way their React code is behaving.


I disagree. The awesome thing about react is it’s simplicity. Hooks can be compared to Rxjs from the angular world. That’s really complicated. I’m working with Rx for over 10 years now, and I’m nearly every time overwhelmed. React makes the concept of reactive programming really easy and accessible. But it’s still something complicated, and it will take people time to learn.

Doing those things is not completely wrong, there is just a better/easier solution.


> If you have to document 12 wrong ways to use your API that's a sign that it's not well designed.

This is so true.


Agreed, but it's a pretty big footgun because `useEffect` is often the seemingly easiest way to do the things you list.

I've had similar conversations many times with coworkers before when they were using `useEffect` to keep a state value in sync with a prop. The officially-recommended alternative of manually storing and updating an extra piece of state containing the previous prop value is cumbersome and also had ways it can go wrong. So, since `useEffect` works well enough in most cases and is easier, often the code just sticks with that method. I'm not entirely sure what's really best all tradeoffs considered, but it definitely illustrates how rough edges often pop up in hook-based React.


You don’t have to store the previous, you can just do an if != right in the render function and call setState there. That’s something I learned from the new beta docs.


Can you point to that in the docs? Calling setState during render sounds like a fine way to get infinite loops.

Any of these sorts of hacks tend to be indicative of something not being structured poorly.


Isn’t it also explicitly discouraged with a warning because of how bad it could be?

The React docs are very clear on this:

> The render() function should be pure, meaning that it does not modify component state, it returns the same result each time it’s invoked, and it does not directly interact with the browser.

https://reactjs.org/docs/react-component.html#render



That’s such a weird 180 on their part considering how explicit they have been about it.

I guess they did a bunch of work to make the case work and are confident about it, but it just seems like such a weird decision considering they’ve been adamantly telling their devs not to do this for years.


Calling `setState` while rendering has been a legitimate thing to do since hooks first came out, but _only_ in one specific scenario / usage pattern.

From the original hooks FAQ at time of release:

- https://reactjs.org/docs/hooks-faq.html#how-do-i-implement-g...

I talked about the reasoning / behavior for this in my React rendering guide:

- https://blog.isquaredsoftware.com/2020/05/blogged-answers-a-...


That still doesn’t change my comment.

It used to explicitly be a thing you must not do for years, it’s weird to go straight 180 on something you actively warn developers not to do. If it wasn’t well documented in the past, then that’s reasonable, but actively spitting out console warnings and explicit documentation warning against it is just going to mean that anyone who isn’t new to react is going to be extremely weary about it.


Interesting. Thanks for following up.


No, because the authors didn't know exactly what they were doing. It can't be that secretive. Even rocket science is explained, just put it in the docs and people will learn it.

For 9 years in a row we discuss core concepts of the library like it's some dark art that only select few will ever understand. It's really not that hard.


Speaking of badly-named hooks, this tweet infuriated me.

If it's legal, the linter should allow it. F$@k "consistency."

https://twitter.com/acdlite/status/1581401077915975680


Define "legal". Calling glibc memcpy with overlapping arrays worked right up until it didn't. The general contract of react hooks is that they may break in unexpected ways if you call them conditionally. The current implementation of useContext happens to not break.


I've run into places where it would be handy to conditionally use hooks, but never the useContext hook, so I can't imagine feeling strongly about that case.


Edit: okay, the docs show cases where there are no side-effects. For me it’s completely obvious not to use a hook for that…

Aren’t useState and useEffect the two main building blocks for hooks (besides useContext)? Everything is derived from those two.

So I don’t get how it would be wrong to handle any of those things with useEffect. It may not be the most easy to read code, but in the end it’s all useState and useEffect under the hood.


useEffect is precisely for getting/posting data. That is one of its purposes. If the react team is acting suddenly like it isn't they are ignoring... reality?


> useEffect is precisely for getting/posting data. That is one of its purposes.

Where is this said? Because everything I've read has been that doing any non-idempotent action in an effect will cause bugs. Therefore, GETting data is fine but POSTing it is not. I've read this even before these new beta docs.


Specifically a component can mount and unmount at strange times. If you expect use effect to only run once you may run into problems.


You are right. I messaged too quickly. When I think about the times I've used useEffect it has been to GET data for populating a page (as I recall). It has been a little while since I have done frontend hook dev.


If useEffect isn't for fetching data, what is?


It can be for fetching data: https://beta.reactjs.org/learn/you-might-not-need-an-effect#...

However most modern frameworks use something like React Query, GraphQL, etc which handles synchronizing local state with remote data for you.


Yeah and those modern frameworks use useEffect lol.

This "3rd party library by default" bullshit is killing me. I just read that link, it's basically "here's a general problem with data fetching that every front ender should thoroughly understand OR actually you know what just use a fucking library and forget all about it".

The React docs have been full of this rubbish for a few years now and it's just straight up irresponsible. Worry about what your own library does, and if there's footguns or complexities then explain them thoroughly. Don't just handball them off and treat your API consumers like children.


> This "3rd party library by default" bullshit is killing me

I've noticed that new developers tend to think of libraries are indivisible units. They never stop to think what React Query uses, it's an atomic unit that you use to do queries, duh.


Yeah well React's "custom hooks" documentation doesn't help matters.

In reality React doesn't expose public APIs for creating your own useEffect; just the base hooks you can compose into "composite" hooks.


Isn't this like the whole point of abstractions? Sure they don't last forever but still.


Well, yes, but new developers need to learn to always delve one layer beneath any abstraction they're using, and understand it. Usually they learn this the hard way by fighting with some abstraction until in a fit of insanity they dig beneath the surface (looking at the library source code or something) and realize that either (a) the docs were lying or (b) the code has a bug or (c) their understanding of what the abstraction was even capable of was wrong.


I don't really see the issue. The page is just pointing out several anti-patterns with useEffect.

The one talking about fetching data recommends a 3rd party library to make it simple but tells you how to avoid certain issues if you want to go the route of implementing your own fetching.

The POST example talks about how some people will modify a state variable to tell useEffect to send a POST request when you can just do it directly.

Maybe it's not to the depth of your liking but I wouldn't call them rubbish.


>The one talking about fetching data recommends a 3rd party library to make it simple but tells you how to avoid certain issues if you want to go the route of implementing your own fetching.

yeah, making it into a custom hook lol.

When did something that was originally worth 5 LOCs turn into 20 LOCs


> When did something that was originally worth 5 LOCs turn into 20 LOCs

When you want it to work properly in all the cases. That includes when new data is being fetched and you don't want to show the old data. Or when you want to not refetch data if the conditions for refetching are not met. Or when you want to cancel an in-progress data fetch if the data shall no longer be shown after fetching it because the user closed the component tasked with showing the data.

I bet your 5 LOC can't do some of these things.


>That includes when new data is being fetched and you don't want to show the old data

This only really happens when you have race conditions

> Or when you want to not refetch data if the conditions for refetching are not met.

This only happens when you fetch based on state update on the same component

> Or when you want to cancel an in-progress data fetch if the data shall no longer be shown after fetching it because the user closed the component tasked with showing the data.

you can do this with certain conditionals and a cleanup function

over 3 quarters of my data fetching is just a simple fetch on onComponentDidMount

do you mean to tell me I have to bring in a library just for that?


> This only really happens when you have race conditions

Or when the network is unpredictable, you retry because it's taking too long, then the second request succeeds, then somehow the first request succeeds.

> This only happens when you fetch based on state update on the same component

Or on props update.

> you can do this with certain conditionals and a cleanup function

That's no longer 5 LOC.

> over 3 quarters of my data fetching is just a simple fetch on onComponentDidMount

Congratulations. Do you also do that for authenticated requests? If so, either you are using some kind of hardcoded global variable, or you are not using 5 lines of code.

> do you mean to tell me I have to bring in a library just for that?

Of course not. You can implement your own library for that. Or copy-paste it all over the place.

What I'm saying is that for things that are more advanced than basic data fetching, you may want to bring better tools than just fetch + 4 extra LOC.


>Or when the network is unpredictable, you retry because it's taking too long, then the second request succeeds, then somehow the first request succeeds.

This has an atomically low chance of ever happening, and never happening on single threaded monolithic servers.

>That's no longer 5 LOC.

Okay cool, 7-8 LOC, better than abstracting it into a whole new custom hook.

>Congratulations. Do you also do that for authenticated requests? If so, either you are using some kind of hardcoded global variable, or you are not using 5 lines of code.

You need to be more specific about authenticated requests, because cookies that are sent back as a response will have directions on how they should be sent back to the server on each request that requires no javascript at all.

If you're using token auth with localStorage, which probably isn't a wise idea because you're now susceptible to XSS attacks, then you should already be abstracting that away.

>What I'm saying is that for things that are more advanced than basic data fetching, you may want to bring better tools than just fetch + 4 extra LOC.

here's a pastebin of my wrapper around requests

https://pastebin.com/V33F1f2F

it is already highly configured and abstracted.

All i want to do in the useEffect is await api.getGamesByDate(), and this should be fine.

What you're suggesting is that you support the React team's decision in that I have to have an additional wrapper around my apiCalls just to avoid two fetch calls in Strict Mode.

Does that sound logical to you?


Congratulations on your code! You created apiCall, effectively your own library for doing requests. The very same thing that you ought to banish.

Once you have setup that library (or hook) yes, using it it's indeed 4 lines of code. That also happens for libraries constructed by others.


> However most modern frameworks use something like React Query, GraphQL,

imo most projects do not need and are made worse by that bloat.


I think most react-based projects that need data fetching should absolutely be using SWR (https://swr.vercel.app/).

It's light enough to be worth it from a very small amount of API calls. Having caching and loading states is a significant UX bonus very quickly.


Is it really lighter than react query?


Agreed, but when you have this many “don’t do what Donny Don’t does”, that may be a smell. I use React a lot and like it, but I do feel that way sometimes. I like that these docs are at least prescriptive and tell you what you should do instead.


Interesting, most of your comments are dead, I just vouched for this one.

Anyway, yeah it might be a code smell if everyone is using them wrong, but I'm not sure how the React team could fix it, at least without breaking changes or moving to some other paradigm.


> Anyway, yeah it might be a code smell if everyone is using them wrong

At least part of the “wrong” use is the recommended best practice has evolved over time, so following the recent recommendations would be “wrong” by the current recommendation.


It’s kind of funny how cryptic react can be. Very easy to miss that having an empty [] as a second parameter will result in completely different functionality.

React is pretty nice overall, just why do we design things to be so unintuitive. Dark patterns.


The irony is that the whole sell for hooks were that they were easier than classes, which people argued were too hard to reason with. There has been so much hype for how much easier things are with hooks and yet here we are.


I agree it's not easier, but I thought the main argument was better composability. And I think that it achieves that goal quite well.


Hooks allow functional components to be used for non pure components.

Which is a huge win IMO because the whole class/object paradigm in JavaScript is broken, and tracking what ‘this’ might be is literally impossible.

I think that’s sufficient reason to use hooks.

Hooks bring a completely new paradigm and I think it was brought out of Beta too early, so React now has to stick with certain concepts that appear bad ideas in hindsight (a fairly simple and relevant example would be that running useEffect on every render should not have been the default…I suspect the React team could have swapped the behaviors for empty array dependency and no dependency parameter, and it wouldn’t be any more logically weird than what we have today and the default behavior would have been far less footgun-ny).

I do think the general idea behind hooks is excellent. I think some of the existing choices, however, need a significant rethink, with the additional real world experience the React devs have with it now.


> Which is a huge win IMO because the whole class/object paradigm in JavaScript is broken, and tracking what ‘this’ might be is literally impossible.

I chuckled. `this` is a piece of cake to understand compared to hooks.

`this` is a function argument that is typically passed to the function by placing it left of the dot at the time when you call the function. That's all you need to know - now you understand `this`.

Hooks are a whole nightmare in comparison - there is a stateful counter that assigns an index to every `useState` call just to determine which result you need to get back. This is a terribly error-prone design that passed review with lots of dubious justifications.


There is a fifth dimension beyond that which is known to man. It is a dimension as vast as space and as timeless as infinity. It is the middle ground between light and shadow, between science and superstition, and it lies between the pit of man's fears and the summit of his knowledge. This is the dimension of imagination. It is an area which we call Hook Hell.


I've seen people reference that "`this` keyword is confusing" argument it has to be something people picked up at a conference or on twitter and just repeat.

And if they really wanna go there, it was the React team itself that took away method auto-binding when they forced everyone to use `class` keyword. Mixins were already available in `createClass`, and if they wanted privacy between mixins, that's totally do-able with Symbols.


Always use myClassMethod = () => { ... } style syntax in classes, and stop worrying about 'this'


With hooks we have now to dialects of the same framework. It is so confusing some time!


You can make it a linting error. There’s a lot of functions that can behave wildly different in very subtle ways. I always enjoy quizzing people on what breaks about this and why it’s not obvious:

    myStringNumbers.map(parseInt)
Because of this I find it very smart to tool up with opinionated linting rules + TypeScript. Won’t catch everything but it covers a lot of easy mistakes that exist everywhere.


Yikes, I had to look up why that doesn't work as intended. For others, map passes the value, index, and full array to a function, which is a noop for a fn of 1 argument, but parseInt takes a second argument, the radix to parse. So each iteration actually calls parseInt(value, index), which gives unexpected values.


IMO this is a more fundamental issue with 'map'. A separate indexed map would make sense because you expect functions passed to map to take one parameter in almost all cases and usually when you need indices you should just use a loop.


Some languages use `enumerate` for this. Maps, filters, for loops etc all work on single items, but enumerate wraps them into (index, item) tuples when needed.

https://docs.python.org/3/library/functions.html#enumerate

https://doc.rust-lang.org/std/iter/trait.Iterator.html#metho...

https://docs.julialang.org/en/v1/base/iterators/#Base.Iterat...

Amusingly in Haskell it only takes nine characters to define enumerate, so there’s not much benefit giving it a name!

    enumerate = zip [0..]
https://stackoverflow.com/a/6473153/119271


I agree with you. I ran into that issue once. Of course I knew map takes a function that it passes value, index, array, I just forgot for a moment. It would arguably be better to have different functions for those.

Very tangentially related, Apple's Metal API has a function that copies a texture. You pass it a width, height, etc... But, if the texture is compressed, then 255 of 256 possible value combos you pass it will be invalid since compressed textures can only be copied in block multiples. I think it would have been a better designed function if it only took width and height in blocks instead of pixels (with uncompressed textures defined has having 1x1 pixel blocks). Then this nonsense of 255 of 256 values being bad would disappear. There's a ton of other inconsistencies in that function. For example, you pass it destinationBytesPerRow when copying to a buffer but if the texture is compressed you pass it say 40 rows and it will only only actually copy 10 rows of blocks and only advance the destination every 4 rows instead of every row. It's arguably a poorly designed function. Thought, I suspect it was inspired by similarly poorly designed functions in other graphics APIs


I can’t speak to Metal, but you’ve gotten at what I wanted to be contrary about in my sibling comment: number (or equivalent, and same goes for other primitives) is an inadequate type. If it’s an array index, it’s not just any number. If it’s a range restricted count of something like pixels, it’s not just any number! Any code in almost any language can box these types so they’re safe to use, but almost no one ever does except in ML langs (or maybe Javaish ones) because across the board it’s a huge bunch of ceremony for mostly worse performance.


I was going to take an even contrarian-er view but after typing it out three times I have to agree, it would be better to just separate them by arity and intent. I frequently use the index parameter, but I never do with a function reference because it’s a footgun from below. And mapping over entries is a well-established pattern, at least enough so that it’s worth the inconvenience of having to do it explicitly.


What is and what should be are separate discussions. But if we want to delve into the topic of what should be, I don’t think adding many different functions for all these cases where this can happen makes sense. It doesn’t scale. The problem, if one believes it’s a problem, is that javascript and typescript are fine with optional and implicit undefined arguments so they never require argument count to line up.

Just use an arrow function to wrap the callbackFn.


We’re talking about one additional function and I don’t think it’s reasonable to extrapolate it to many others. Which is why I started at your position but talked myself out of it trying to make the point.


I've found that people that learned class-based React first and then switched to modern function-based React often have more trouble with useEffect than people who learned functional components directly

I think a big part of the problem is we need to stop selling useEffect as a replacement for the lifecycle methods of class based components. It also probably would've been a lot less confusing if we called it something like useSideEffect

I think any react linting setup resolves most of the confusion but there's a lot of people that start off and don't even know how to set up lint rules for react. They should be a default in any react app imo


> I've found that people that learned class-based React first and then switched to modern function-based React often have more trouble with useEffect than people who learned functional components directly

I’d say its the opposite; people who used lifecycle methods know that useEffect and friends eliminates an entire class of bugs. People who started with hooks dont understand the problems it solved, and only see the quirks


This was not the only design possible. The main problem with lifecycle methods was that you couldn't have multiple of them. There is absolutely nothing about the design of React that necessitated hooks to fix that issue.

You could have this API

  this.addListener('mount', () => {
    // do things on mount
    // return cleanup to be called on unmount
    return () => cleanup()
  })
called from the constructur of a component. With this you could setup multiple listeners and make sure they're all cleaned up.

But no, the syntax wasn't "clean" (you'd pass `this` as an argument to the "custom hooks" equivalent"), so instead we got this error-prone order-dependent design that doesn't allow conditional execution and runs on every render.


I'm far more experienced with React function components than with class components, so I'm curious, what are some of the problems with class components and how did hooks solve them?


I can second this... I inherited a huge pile of crap without a react lint ruleset and as soon as I put it in place there were ton of errors.

The part I don't like about useEffect is that developers tend to overuse it and when they get stuck in infinite render loops you can see the whole mess it can lead to and how hard it can be to untangle monkey patched logic.


I think that particular choice was an unusually big mistake. Most of them are not so big. But I can't fathom why they made "empty array" cause no rerenders but "no array" cause all the rerenders.


useEffect is treating undefined as meaning undefined.

It makes perfect sense from inside of useEffect.

If there are no dependencies ([]), the dependencies can never change, so React can always reuse the old effect.

If the dependencies aren’t defined, there’s no way to tell if the old effect is OK, so React must always rebuild it.

**

But `useEffect(()=>{})` doesn’t explicitly show that React will get `dependencies = undefined`, and in this case that’s unusually important.

You could have ESlint force you to change that to `useEffect(()=>{}, undefined)`, I suppose…


Yes we know how it works. What we’re saying is that it is a cryptic design. Just like the function returned from useEffect is used for unmounting the component. Not intuitive at all.


there is an eslint rule exhaustive-deps which really helps with this


For useMemo and useCallback, Typescript and exhaustive-hooks will both tell you to add a dependency array, because without it those hooks do nothing.

For useEffect, exhaustive-hooks & Typescript say nothing, because a non-memoised Effect is a reasonable thing to write. It’s just not a great thing to write accidentally.


There's a few, rare, use-cases where you'd actually want this behavior. But I agree it's too easy to accidentally do this thing that should be an extreme edge case.


I've run into those cases a couple times before and my immediate thought was that it seems unwise for this one function to be used in both cases. There really ought to be something specifically intended for effects with no dependencies even if all it does is make the difference explicit. I'm not a fan of things that contain features that manifest like subtle traps because determining programmer intent is undecidable. That lesson was learned from PHP <=5.


This is why whenever I can (i.e. it’s not a breaking API change or it’s agreed on), I turn these implicit rules into explicit options named and documented as such in the code. Yeah it’s a little more to type, but it solves the “this isn’t obvious for good reasons” problem much better than a pair of defaults that you probably don’t even know how to look up.


At the very least make them separate functions


Yeah, that's what I'm saying. There's no reason they had to be the same thing.


I'm pretty sure because it's basically an easy hack to recreate the behavior of componentDidMount.


Dark pattern implies malice. IMO this is at worst poor forethought.


The original design was fine, the hooks redesign was a total miss


It's a design choice to import the functionality of componentDidMount into the hooks pattern


Abysmal framework with horrible documentation. On one hand React boasts that it's just JS but on another puts so many constraints on how you program which are nonsensical. (don't use useEffect in for loops)

I have yet to see an in-depth treatise on how it works under the hood. All I get are surface level posts of guidelines and crappy posts on FreeCodeCamp written by HS students who learned how to program yesterday. State management is another nightmare as well.


> I have yet to see an in-depth treatise on how it works under the hood

Check out “A Complete Guide to useEffect” [1] by Dan Abramov, one of the core React developers.

[1] https://overreacted.io/a-complete-guide-to-useeffect/


See my extensive post "A (Mostly) Complete Guide to React Rendering Behavior":

- https://blog.isquaredsoftware.com/2020/05/blogged-answers-a-...

as well as these other excellent posts on React's internals and implementation:

- When does React render your component?: https://www.zhenghao.io/posts/react-rerender

- A Complete Guide to useEffect: https://overreacted.io/a-complete-guide-to-useeffect/

- Getting Closure on React Hooks: https://www.swyx.io/hooks/

- Didact: Build a Miniature React with Hooks" https://pomb.us/build-your-own-react


Can someone explain why React can't have a more ergonomic syntax which compiles to useEffect()? Like svelte(reactivity) and vue(script setup and ref sugar) can compile their ergonomic syntax to actual apis

Given the dominance of react, "switching to vue" (let alone svelte/solid) is not a viable option for many. But surely someone can shamelessly steal from them?


One of the great qualities of react is to not be compiled, which removes an entire layer of confusion. It's a tradeoff but it is makes it a lot more predictable.


React is actually getting a compiler in the future for memoization: https://www.youtube.com/watch?v=lGEMwh32soc


I had not seen that, thank you. The compilation being optional, backwards compatible, and producing readable / step-debuggable JS seems like a really nice approach.


WTF!!?

"Not to be compiled" is not a feature. It is a lack of feature. Would you say it is better for everyone to ignore typescript and use js directly?


TS is transpiled, not compiled (at least most of it, there are exceptions like enums and certain ES features depending on target).


It was called MobX and it was getting more and more traction before the hooks hype.


It seems from these guides, and the comments here, that hooks are pretty confusing. For my part I learned several new things from the "you might not need an effect" doc linked elsewhere, and I consider myself to be an expert-level React developer.

There is obviously something to be said against hooks being "functions, but with special requirements", and things like React safe mode actually exacerbate the problem (it used to disable console.log on the double render, leading to variables literally changing under your feet; now you just get double console logs for unexplained reasons).

I'm curious if the React team experimented with an alternative set of lifecycle methods for class-based components. Like, add a new base Component class, but have the constructor freeze itself to outlaw instance variables, and require going through a useState-like API to get that functionality. What would it look like?


useEffect should not be capitalized because it's a function name (I guess HN's title auto-mangling did this).

While we are at it, I would suggest to specify in the submission title that it's about React, and it's also from 2021, so:

  A Visual Guide to React's useEffet hook (2021)


I don't understand why we're still stuck with hooks. JSX was a syntax change introduced to support things that were hard to express with existing JavaScript syntax, why not do the same instead of hooks? Using function-call notation to create hooks seems like a kludge, and if you are going to process your source with an transpiler and add browser-incompatible syntax like JSX, why not do hooks the same way?

This would allow us to avoid all the gotchas with non-pure functions in useEffect, breakage when calling hooks in conditions or loops (hoist them!), and whatever restrictions might come next.


This is exhausting


Can you send this to the React team? I think this would be extremely helpful for the people that are learning React from random YouTube videos. I really like how you've created these visuals


React confusion (and the resulting pontification) has got to be one of the main things keeping the "aspiring developer" business going.


I just revisited reactjs docs beta website, and can't find out how to fetch data from remote server ! It's a shame.


React.js is one of the leakiest abstractions I've ever seen. And the ecosystem is horrendous - it makes things slightly shorter to code at the cost of astronomical operational complexity.

I tried to drink the Kool Aid, and I got paid to write it, but it's just a bad idea to build a parallel abstraction, that leaks like a sieve, that needs its own browser tools, that pretends to be reactive but really isn't. I was so much more productive when I used vanilla js, plus the odd library as needed. And if you tell me react is "just a library" - great, remove it from your dependencies and tell me what % of files you now need to fix, because I've seen multiple react projects in the wild and they all look the same.


What had always worked for me is that I write the web app as regular classes / objects, make it observable with mobx, then add React as essentially a template library — like as if it was mustache.js or something.

The non-React code works on its own and is super easy to understand. You could even extract the business logic and make it a CLI or a reusable library.

Shit, all my apps - Java, C++, whatever - have always been structured like this: domain logic + a decoupled frontend. I pretty much only adopt libraries that match my way. Redux did not match my way

My coworkers from past jobs tell me the way I structure my projects is so clear so I think I’m on the right track


I think that arch is the best and I've used it for low interactivity projects where the frontend is almost all display. Haven't tried it for complicated SPAs with lots of DOM interaction though.

Do you have any such experience? How do you architecture your models, events, processing, updating, etc? Do you have UI-less component-like pieces of code that you can compose? Or a fat root state manager that does everything?

Not sure if you work on any public repos, would love to have a look if you do for inspiration :)


I don’t have a repo to show but I’ll try my best to explain

First I want to note that I do use React hooks. If I can some UI code in React, I will. DOM code would go in React

I just won’t put business logic in React ever. No non-UI side effects and no external state are allowed in my hooks

I still want my React components themselves to be a reusable library. My applications are basically a reusable logic library coupled with a reusable UI library. It’s that division that makes this style easy to read - your brain is either in UI mode or business logic mode. You never confuse yourself trying to figure out what it’s doing because you can just read one side and ignore the other

Whether you use a fat root state or not is honestly up to you. It’s really the separation that is key

I’d check out mobx examples

I know that’s not super specific and there are edge cases that you’ll run into, but my public repos are either Java and non-UI JS libraries ):


Stores for everything. Modal stores; enforce open modal business logic app wide. Menu stores; allow customizing menus via config or plugins. Event store; app wide event bus now you can know when specific components/widgets rendered for the user. Theme store; wrap light/dark media query for system pref and user override in an observable.

Keep any and all business logic out of components if at all possible; that's my moto.


I don’t understand why people moved away from this. It doesn’t even have to be mobx’d ideally, an informal emitter protocol would be fine, just as “rerender all reconcile the changes” mode for pages with 10k divs or less. People use a concept-heavy application framework and pretend it’s “just a thin ui library” because kilobytes. Absurdity.


I've done similar! Worth checking out googles lit-html if you want just a template library.


I see many complaints about the react APIs.

Personally, I like the way react is balanced. Yes, it can feel unintuitive or complicated to beginners, but that's because it puts a lot of emphasis on experts productivity and ergonomics while only using js, not being compiled.

Once you do things the "react way" (whatever that means, you'll get there if you take time to identify and eliminate "smells"), it just feels so... smooth? You can build everything with the same methodology and flow. Simple components can feel a bit over engineered, but the hard ones ones feel much simpler than they would be in other stacks, and upgrading code and functionality feels effortless.


Not very visual for a visual guide.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: