You might not need an effect
react.dev
react.dev
I love the terseness, reusability, and typing of React hooks, but hooks have too many weird edge cases (this article, dependency list, order of hooks, etc) versus class components, whose design was simple and elegant.
I'm just an old man yelling at clouds though, the terseness of stateful components defined in a function, plus simple typing with TS (no defining proptypes) is too appealing to me personally. Maybe I'll check out Solid next time, which seems to have less weird edge cases.
It's in need of a few upgrades though, namely a few edge cases in the old JSX parser (it was written before Acorn had good JSX support) but works pretty much perfectly still today.
Performance is nowadays one of my last concerns with frameworks. They're all snappy enough except for corner cases. More important is whether there are standard ways of doing things vs everyone inventing their own. Our company uses Vue which has standard lifecycle methods, standard way of scoping CSS etc, officially endordsed store etc. Makes it much easier to bring people up to date when they're joining the projects.
Whats the alternative you propose? Not allowing react to be used with any 3rd party libraries or access to browser apis?
Heh, we already have that and it is called Elm.
By me, an Elm user until 0.18.
Ok... no... there is no good way to shoot yourself in the foot (metaphorically or real)
>There's never a situation when react doesnt allow you to do something
That is EXACTLY what I DONT want from my framework:
Unless you're happy about never going out of the guardrails set by your framework, there's going to come a point where, one way or another, you're going to need to interact with something outside of it. Are you going to wait for an official React-WASM way to interact with WASM (which is going to be using useEffect under the hood anyways)?
Frameworks with no escape hatches are hell. The escape hatch just needs to be bright, bold and with a warning on it that says "there can be demons behind that hatch, watch out".
'Escape hatches' are definitionally exceptions to the rules, whereas effects in React are standard practice.
x1000 upvotes.
One alternative is of course svelte, the unofficial-slogan (svelte) is/was that you can explain it to someone or learn it yourself in an afternoon.
Reminds me a lot of C++ vs Go in terms of Public/Private/XYZ
Go: To make a something public, start the var name with uppercase.
C++: To make things public read the Chapter on "Access Modifiers".
Phrased differently:
Go: To make a something public, start the var name with uppercase.
C++: To make things public, type the word 'public'.
The Go pattern seems like more of a sharp edge than the C++ version, even though the C++ version might need more reading to learn.
You mean from private to public?
In that case, you would just have to change a single package.
(also, if yes: does it happen often and is that really what we should optimize for then?)
Re: optimization, what does using casing instead of an explicit `pub` prefix optimize for, exactly? Compiler complexity? Typing ergonomics?
Can you explain that to a newbie in 2 sentences ?
I'm so glad I don't have to deal with it anymore. After being in programming for over 20 years, I started making plans to exit the entire industry. Now I realize it was just react. That thing made me despise programming. I kept a virulent twitter journal of my time in react. It seems foreign to me now. Good! Example(s): https://twitter.com/SisNode/status/1458942812087414797 ... it made me pretty intolerant https://mobile.twitter.com/SisNode/status/143380932394827366...
The tweets in that account are like an insult comic does programming.
I mean sure, the passions are real but I'm also ostensibly a well behaved sincere adult. I don't actually want to "literally stab the people who wrote [apache modrewrite] repeatedly in the eyeballs" - although a reconsideration of how transparent the flow is would be greatly appreciated.
Perhaps I should add that to my list of "things to do" and you know, fix it myself. Be responsible or something. I've become intentionally unemployed in order to spend some time on stuff like this and regroup. I should do it.
Honestly, I'm probably still recovering from that react project. Maybe I should do some therapy. It was bad.
You said you keep it private and try to be well behaved, but it probably comes out in your interactions with others, whether you're aware of it or not.
I used to do comedy when I was much younger and I've always had a love for it. I've tried the unhinged neckbeard character a few times. For instance, here's 11 years ago https://mobile.twitter.com/gitHater/status/16997810622706892...
Here's authentic me for comparison https://mobile.twitter.com/emoRobot
this model clicks so perfectly I still wonder why it's not the standard.
It's the same old OO (state+methods) vs FP (lexical scope + closures) thing, in the dom now.
A lot of react state libs are also tweaking around this idea.
Not long ago a jotai class made me see this.
<Component />
<Component />
these components use the same state defined in the sourceso to avoid that
<Provider>
<Component />
<Provider>
<Component />
which now intercepts the state object creation to have two independent states. I have a feeling that soon we'll see <new class={Component} />
strangeMost bugs I find in big react projects are either by using multiple useEffects in one component, and the other one with Saga. Also another "side effect" tool.
Don't get me wrong, I have fond memories of using classes as bags of methods (esp with Mobx) and it took me a while to come around on hooks. But given your "most bugs... useEffect... and Saga" (does anyone still use Saga?), maybe the over/misuse of useEffect -- not hooks / FC more generally -- is the crux of the problems you've encountered. Which might even reinforce the OP (shrug). :)
So lifecycle methods had some other drawback? Yes, sure, but they were less than the drawbacks of hooks for most people.
But these confident assertions about what's "right" or "horrible" are subjective, personal preferences.
And unsubstantiated claims that they apply to "most devs" or "most people" is merely a projection. That contradicts my direct experience (I've never encountered a single team that adopted hooks and went back). Hooks' adoption rate (along w React's market share) suggest I'm not an outlier.
All that said, I'm glad TMTOWTDI. I also want to pause and thank you for sharing your perspective; dissent is valuable and helps prevent groupthink.
Have a great day!
Just look at the exponential growth of Solid: https://npmtrends.com/solid-js
This will happen fast. I'm not even using Solid - I like the html first approach of Svelte - but the writing is on the wall. The data is saying as much.
I was using Angular in 2013 when React came around. I knew Angular was dead as soon as I saw React. Took the Angular people many years to realize this, and many still haven't. I'm saying React is already dead. Svelte and Solid killed it. It will take a few years to play out, but you can see it happening already if you pay attention. The React devs are doubling down on the inherit complexity of React instead of cleaning it up - this is exactly what killed Angular and made people move on to something simpler.
I am extremely confident that this comment will age well, but we'll see :D
How can you be so sure that growth isn't just riding a larger wave of web dev growth in general?
Remix react and has a steeper curve: https://npmtrends.com/@remix-run/react-vs-solid-js
Nextjs is still growing fast.
What's the stats of Solid apps in production vs people just doing tutorials?
Dan is very smart, also redux was very smart but ended being an overkill. Side effects are a concept only thought at the Uni.
That's the core of the issue, useEffect is too complicated. As well as Saga. Even for simple requests it can become tricky to figure out what the flow is.
There's really not that much difference.
useEffect and dependencies are the worst part of React at the moment.
The reason is, you do need to use effects, everywhere in your code, constantly. And dependencies, and the linter rule that goes with them, are nasty ergonomics, and hard to reason about.
The article I linked does explain it all quite well, and most devs I work with do understand the dependencies rules, but the rule still needs to be disabled often. Why?
Often the lint errors really are just wrong. Just like ChatGPT is often simply just wrong, no ifs or buts. For example: you have a callback function declared as an expression, that the useEffect hook calls. The linter will say "oh this is an expression, therefore it needs to be declared as a dependency". But it doesn't, because looking at the code as a human, you can immediately see the function never changes.
If you follow the linter, you will inevitably end up going down a destructive and unnecessary refactoring. You'll end up with unnecessary `useCallbacks` sprinkled everywhere that hurt readability for nothing. Often, you'll have to refactor the dependencies out of the component entirely, splitting one cohesive component into two, just to satisfy the linter out of an abundance of caution.
Junior devs will be confused. Senior devs will be frustrated. Every react codebase I've worked on has gagged this linter warning.
So how should this work?
It shouldn't be a a linter error in the first place. Linters should be used to enforce coding standards, not fix application bugs. I feel very strongly on this.
let count = 1;
// this is reactive
$: doubled = count * 2;
function handleClick() {
count += 1;
}It makes it so that the same instance of a function is used for all renders, but that it always points to the data closed over by the latest render; it eliminates a whole class of scenarios that normally lead to gnarly dependency array tomfoolery with useCallback, useMemo and useEffect.
It's quite a common hook to try to invent in user-land (i've seen it in numerous projects), it's not just a linter silencer.
It's my understanding that this was to be the behavior of the canceled useEvent hooks but that the new useEffectEvent hook does not return a stable reference.
Do you have sources for the behavior of the new hook?
> Here, onConnected is called an Effect Event. It’s a part of your Effect logic, but it behaves a lot more like an event handler. The logic inside it is not reactive, and it always “sees” the latest values of your props and state.
Here is the test suite for useEffectEvent and the test ensuring it does not provide a stable reference: https://github.com/facebook/react/blob/main/packages/react-r...
Edit:
So it's kind of interesting, this test (and the functionality behind it) exists specifically to make sure you can't rely on it having a stable identity -- and therefore have to omit it from the dependency array. But it otherwise behaves the same as the popular user-land implementation involving refs (which you still have to include in the dependency array, because it works by providing a stable identity).
So there's some internal trickery in React to make sure that even though the effect event is recreated and has a new identity on every render, any time it's invoked you're accessing the latest inner callback.
Now it's just going to focus on the effect issue.
[1] https://github.com/reactjs/rfcs/blob/useevent/text/0000-usee...
Can you elaborate on this? In my experience this isn't the case. If a function is declared outside of the component scope then it won't trigger a lint error. If it's defined within the component scope and not wrapped in useCallback then it does in fact change on every render and the lint error is being raised for good reason.
So I just did some playing around to investigate this further, and it seems to be more subtle than I thought.
If you have a function expression but the expression is not dependent on any outer scoped variables, the linter does not complain that you didn't specify the function itself as a dependency in useEffect. Example:
```
let bar = "bar";
const setResult = (result: string) => {
// Won't trigger "exhaustive-deps"
setBio(result);
// Will trigger "exhaustive-deps"
if (bar === "1") setBio(result);
};
useEffect(() => {
setResult("");
}, [person]);
```So there's some quite sophisticated analysis of the AST going on in the lint rule.
I still think that this rule is too complicated for a linter. (Bugs like inter-dependencies in your data and code should be caught by unit tests).
But it's technically pretty impressive.
Edit: I did some more investigations.
If I make a function that depends on something very unpure (like checking window.location), the linter fires if the unpure function is in the outer scope of the hook, but not if it isn't. Even though the dependency is the same. So the linter rule can only identify locally scoped dependencies I think.
Why write a ton of unit tests for what a static check can just do “for free?”. Not sure i follow your logic. I agree React can feel like a nuisance, but the lint rule itself is not the issue i think you have, it sounds like your issue is just with hooks.
window.location returns a Location object which is not a primitive, and so it will trigger exhaustive deps if it is being used by way of a reference inside the component render function (assigned to a variable), since it could theoretically be a different object reference on subsequent renders.
Since the linter can't necessarily tell if the reference has changed out from under it on subsequent renders due to the fact that it could be mutably changed, it marks it as a necessary dep.
This is because props or state didn't change and the parent didn't rerender.
UseEffect dependencies aren't observables. You'd need to wire up an event listener on unload, hashChange, etc
https://stackoverflow.com/questions/58442168/why-useeffect-d...
As with everything, if you want to make an apple pie, make a universe: after all, the mistake was mine, starting to use React in 2015 instead of making my own language/framework, mainly because what I have today is already a bunch of highly idiosyncratic React utilities. If there is one hope with the large language models hype is to finally be able to code against a true common interface (the HTML/CSS/JS specification), and do it anyway you please, translating on the fly any third-party dependency.
[1]: Note that this docs effort doesn't include SolidStart, which is a big bummer.
The entire doc is written around two common problems:
* You don’t need Effects to transform data for rendering * You don’t need Effects to handle user events
Both of these problems do not occur if you take the time to understand the tooling React gives you. The events point should be a given, that's just React 101 (and yet they have to spell it out to people...).
You don't end up using useEffect to transform props/state and generate new state if you understand that those things are just variables and you can instantiate new variables using them inside the scope of the component function.
"But that's inefficient because of how re-rendering works".
Great, you understand how re-rendering works but haven't gotten familiar with the useMemo hook. Come back when you've gotten familiar with the primitives available to you in React and how they can all work together in different ways, there's not that many of them!
This doc reads to me like a DIY store publishing an article about how you shouldn't hammer in screws even if it kinda seems like you could if you didn't take the time to understand hammers, screws, and nails. It's sad that it needs to exist but that's the current state of engineering.
But I barely know React.
Think back to the webdev world before react. Was it really that the average dev was more competent, or that the tools of the day were in fact more simple to use? Sure there are more and less efficient ways to use jquery, but unlike react you didn't have to keep so much in your head at once while building a simple feature.
React was designed to be a UI component library. We keeping asking more of it and stuffing more and more complexity into react so that monolithic frameworks can be built with it. It was never designed with that goal in mind and is taking more and more complex features to try to keep that ship afloat.
I think that JSX support is a genius thing. Especially with modern TypeScript. I spent so many hours because of string-typed templates that I really value templates integrated within a language.
I really liked original React version with classes. It had its rough edges, but it was simple and accessible to me.
Modern React with functions is not simple. It's so far from simple. I consider myself pretty talented developer. But those hooks - I think last time I had to spend that amount of time is when I learned Scheme continuations. That thing really broke my mind. Well, hooks didn't break my mind, but they just seems to be very easy on the surface yet very hard below surface. I read plenty of articles yet I still don't fully understand them (although I think that at least I can use them without bugs).
For me React is like ORM. It's very powerful tool. I allows to tremendously cut the development time. But it requires absolutely expert knowledge on the team. And it looks so deceptively simple at the same time. This is a bad place for technology to be in.
With all business logic outside the render tree it makes a ton of sense and really can be understood extremely fast. Props in, events out. Done. Today everything is a component, and components even have to understand whether they are in the server or browser and how to manage that handoff. It's such a massive dumpster fire of convention-as-a-solution.
The trouble with a smash-hit library (or, let's be real at this point: framework) is that adding more whizz-bang features to it becomes irresistible, from a résumé-building perspective. Add in that a bunch of the devs are paid very good money to work on it, and they really do have a lot of incentives to keep finding more shit to add to it, rather than calling it "done" and letting it enter maintenance mode.
Wait, there’s people in this industry who are taking the time to understand things before using them?
Wait, wait. I'm trying to make friends with React for less than a month and I do appreciate this article giving me some understanding about the primitives react gives me and when to use it (Or how you called it screws/nails)
I did understand more, but I'm left with a feeling I cannot follow the "flow" of the useEffect as I couldn't understand how can items !== prevItems EVER be true?
// Better: Adjust the state while rendering
const [prevItems, setPrevItems] = useState(items);
if (items !== prevItems) {
setPrevItems(items);
setSelection(null);
}
useState doesn't return immediatelly? Or does it cause immediate re-render? I kind of gathered from the article that all useState calls cause re-render whenever it encounters return.- the argument passed to useState is initial state, not "forever state". It will seed prevItems for the first render. But later when items prop changes, it's on you to detect the change to what you have as prevItems so far and call setPrevItems yourself.
- items is coming as a prop from outside, prevItems is whatever you set it to last time using setPrevItems
- calling a state setter (setPrevItems / setSelection etc) will always cause a rerender. That's why if you don't have the if condition you have just created an infinite loop - you are inside a render, and if you unconditionally call setX that causes another rerender later etc etc
- that's why I am not a big fan of this particular "Better" example and prefer the later "Best". Avoiding calling state setter function from inside render will make your life easier.
But to summarize that page briefly, useState will always return the current value of the state and a function to set it. But "items" here is being used as the default value that's provided the very first time the component is rendered, when the state doesn't exist yet. In subsequent renders, the state exists, so "prevItems" will reflect the last value that was set with `setPrevItem`, not the current value of `items`
"The tool isn't poorly designed, everybody is just too stupid to understand it."
Which is the problem here. The abstraction in question tries to dumb things down, but doesn't succeed in dumbing things down enough. As a consequence you get their weird middle ground where you can't access the gritty details an experienced programmer would want, but it doesn't hide the gritty details from the inexperienced programmer, ultimately serving no one. As usual, you can make it work, but it is a poor abstraction.
This was a non-issue with class components.
I catch it in code review and show them that when you delete all of it without useEffects and useStates that it just works. Its an antipattern that turns the code into a giant ticking time bomb if you don't manage all of it out.
In a traditional MVC-like paradigm, components would just be views. Discussions would be focused around keeping your components limited to view concerns while using other, non-view parts of the framework to drive things like API integration, business logic, state management, routing config and so on. Which is to say that you wouldn't have a need for "effects" at the component level in the first place since, by the time your data is ready for presentation and your components are initialized, all of that has already been done separately.
So yes, it is a design flaw with the "framework". One that came about by trying to morph what was, initially, a simple view library into something more "framework-like."
You know, I kind of agree and personally prefer Vue (that does hooks better in some ways, as well as has a simple store solution called Pinia that's nice to use), but I have to say that these docs themselves are great.
Even if there is some awkwardness about using React and it needs some getting used to, to be able to use it effectively, I think these docs are definitely a step in the right direction and offer concrete examples! The code is mostly clear, the text explains everything bit by bit - we could use more of that in our industry for sure.
If you ask me, I'd design it the following way:
useEffect(f, {a, b, c});
...
function f({a, b, c}) {
...
}
Function f is supposed to be declared outside of render function.This way you must specify all the dependencies. JavaScript will bite you with undefined value, TypeScript will bite you with compilation error. The drawback is that you'd need to pass setValue functions which are typically immutable, but I don't think that's a bad thing anyway.
That's the cinch. To get the benefit of this proposed API design, the effect handler functions must be be outside of the render function so they can't capture any values other than the explicit dependencies. That's not a requirement you can enforce in JavaScript, so it would have to be a linter-level thing.
I think it would aesthetically run counter to the prevailing design of React functional components. For better and worse, it feels like the framework designers have made a substantial effort to contain a component inside a single function. With this design, you'd have the component and its effect handlers in the same parent scope even though the handlers are subordinate to the component.
Not in function arguments. In function arguments that syntax does destructuring.
And it seems that the React maintainers don't understand their own system, in a sense.
That obviously sounds a bit weird, but as far as I can tell they seem to think that what makes React good is that it is "functional". And so when something isn't quite right, they "fix" it by making things more functional. And that seems to invariably make things worse. Rinse and repeat.
Hmmm...
What if the good part of React is that it is a reasonably decent UI widget framework for the web? And that the FP-ish nature is really more an implementation detail due to the way it is embedded into the web platform? And that this led to the happy accident of being able to write the UI as a procedure that structurally reflects the UI to be displayed?
Thank you. It's self-serving (mea culpa), but this is the exact reason I moved away from React and decided to build Joystick [1].
> dependency list
This, again, is only confusing if you don't understand what an effect is for: syncing with systems external to React. How else would you say "do the thing when any of these change"?
> order of hooks
I never understood why this was confusing for people. It takes 2 seconds to learn this rule, and your browser console will yell at you if you violate it.
https://youtu.be/AdNJ3fydeao?t=702
We really should accept that all of the following are ABSTRACTION LEAKS and not inherent benefits to web development at large:
• shouldComponentUpdate
• React.PureComponent
• useMemo
• useCallback
• Concurrent mode
They exist solely because React's underlying model is insufficient and we, the developers, are being made to do the computer's job with all this optimization boilerplate. The VDOM is dying. Let it die. It's long past time to move on. Any further React development in the existing foundation is just continuing to build upon sand.Hooks are overused in general. But I really disagree with this sentiment. Its useEffect and useLayoutEffect(okay useInsertionEffect too) hooks are THE mechanism they CHOSE to expose component life cycle. It's not an escape from React paradigm, it IS the paradigm. Function component React would be useless without these hooks; the DOM is an external system!
I'm really just put off by the React documentation in general. It feels like it's aimed at junior devs working on FE assembly lines and they want to keep the blinders on them. "Pay no attention to the man behind the curtain!".
https://epicreact.dev/myths-about-useeffect/#:~:text=Here%27...
From the documentation for the useEffect SETUP parameter:
> setup: The function with your Effect’s logic. Your setup function may also optionally return a cleanup function. When your component is first added to the DOM, React will run your setup function. After every re-render with changed dependencies, React will first run the cleanup function (if you provided it) with the old values, and then run your setup function with the new values. After your component is removed from the DOM, React will run your cleanup function one last time.
It’s good advice, because it addresses a major stumbling block people struggle with. After that the code and the way useEffect is used becomes simpler.
> Here's the crux of the issue: useEffect is not a lifecycle hook.
That doesn't leave much room for ambiguity; seems like a definitive statement. Many don't see any nuance here and then go around blasting people for saying useEffect is a lifecycle hook because they read on some blog the exact opposite.
But anyway, what is a lifecycle hook if not a way to synchronize two systems... ( ͡° ͜ʖ ͡°)
Posts like this are almost always insufferable.
There is a very specific way of doing React that influencers and sometimes the React core team push very, very strongly. Sometimes it's justified, but usually I find that it's personal preference being pushed as fact.
I stopped being a React fan with hooks, but remain a fan of Redux.
And these still aren't intrinsically tied to a component lifecycle.
See react's official docs https://react.dev/reference/react/useEffect#what-are-good-al...
They recommend using react-query (a cached fetching library) and react-router. This combination definitely uses lifecycles to fetch data in the render functions.
With react-query for example, when you use useQuery it communicates behind the scenes via Context with the react-query singleton. It doesn't fetch data because a component has rendered, it does so because a useQuery function call has asked for data it doesn't have in its cache.
That may result from a component rendering for the first time, or because that component's props changed, or because some piece of state somewhere in the app changed, etc etc. In the end it doesn't matter. It's not about a component's "lifecycle", it's responding to a change in lowercase-s state.
It does not fetch without rendering, once.
And you don’t need a useEffect to do this sort of thing. You just need a function that creates a promise keyed to whatever identifier you passed in and some state to hold its status and result.
Yes you don't need useEffect. But the alternative is something like redux, where you're components are not (self-contained) components anymore. And the docs recommend effectful libraries.
And no, you don't need useEffect or Redux. You need a promise and some state to track it. Things like react-query aren't "effectful".
So nothing ever renders.
> And no, you don't need useEffect or Redux. You need a promise and some state to track it. Things like react-query aren't "effectful".
Of course a Promise is a side effect. You're talking bullshit.
And a side effect and useEffect are not at all the same thing. You're conflating generic software terms with APIs that have very specific meanings and functions within React.
useEffect is a side effect in a render function. Not all side effects are useEffect in a render function, good job.
I guess the redeeming point is that hook usage is usually easy to refactor out, unless it's permeated your architecture somehow.
Dealt with projects that had evolved(devolved?) into hook monstrosities. 20-30 hooks in god components each with n-layers of "custom" hooks each permeated with useEffects and useStates chaining off each other causing a rats nest of render loops and maddening re-render puzzles.. It's awful.
I'm anti-hook in general and I use them very sparingly. But they, the effect hooks, are integral to React and need to be used to build the vast majority of anything interesting. People will need to use them or use something written by somebody else that used them. To me that's not an "escape hatch" but fundamental to the libraries usefulness.
Yes, but now imagine that you could've done it more or less correct in the first try. Refactors happen, but needing the refactor after it took 2 years for best practices to sink in? I don't think it's an endorsment for the hooks.
I refactored my old React app from classes to hooks when hooks became fashionable. Few years later I'm still sure that it's a mess and I've used hooks wrong. Am I itching to refactor again? No way. I let it rot some more, perhaps React will take another sharp turn and I'll save myself time doing it twice.
If you read this sentence "In normal rendering, React does not care whether "props changed" - it will render child components unconditionally just because the parent rendered!"
and understood that react doesn't re-render a component when its props change, well, I read that sentence in a different way.
useEffect is not an escape hatch, it’s a fundamental building block.
The point of articles like these is that people use useEffect when they don’t need them. So it’s robust advice in that regard!
But you’re always going to use it, directly or indirectly.
For example if you’re doing anything interesting with your layout that CSS can’t exprees, which is fairly often, you’re going to need useEffect in order to read the DOM and calculate your layout.
Is this a problem specific to the documentation, or does it apply to React in general (or at least functional components)? Because I feel like it's the latter.
Yes, and component lifecycle itself is an escape-hatch (or airlock, or whatever).
> Function component React would be useless without these hooks; the DOM is an external system!
It is an external system, but most fundamental idea of React is that you don't have to write your own effects or lifecycle logic for updating the DOM (the great majority of the time). That's the whole point. The way React lets you update the DOM without using a side-effects programming model most of the time is the reason people use it. Instead of writing side-effects toward the DOM, you write a unidirectional flow of values derived from other values which bottoms-out in DOM (which React then "affects" for you). Custom effects are (mostly) for the situations where you're updating an external system that isn't the DOM.
The biggest problems in my experience come when effects become a part of the core domain logic. That's where things get really hard to follow, and that's the most important thing to discourage. Effects should be reserved for the boundary between the app and the outside world (minus normal DOM updates).
To paraphrase an old quote, “If you see bad code with your utility all day, perhaps your utility is the bad code.”
Joel is a great but credit for this goes to Rico Mariani, originally quoted here https://web.archive.org/web/20100514093413/http://blogs.msdn...
</aside>
https://twitter.com/dan_abramov/status/1638773531881140224
> fun fact: You Might Not Need an Effect was inspired by a little investigation. i spot-checked random 128 useEffect calls in the Meta codebase, classified them into several groups, and 59 out 128 were unnecessary.
My own experience, on a much more limited scale, is similar. Especially developers new to React tend to write more useEffects than actually necessary. These often add unnecessary complexity and make the component much harder to understand.
I recently reviewed an architecture on AWS that has 5 services chained together to make information flow from source to target.
There was no justification for the middle three. The volume of data, loose coupling, native support for direct integration were all there to directly connect source to target.
Because of that, they quickly reach for the escape hatch that useEffect provides everytime they need to change something.
I genuinely don't know how to help my fellow team members grasp the basic concept of components better. It doesn't seem, to me, like it's something everyone gets eventually. I've actually seen far too many developers who just don't seem to get there even after long months working with component libraries.
As a side note: I don't get all the love for class components. All I remember from using them were the endless bugs because we somehow missed something on willUpdateProps or whatever. Class components could have worked but they always felt like the different life cycle methods were all cobbled together with little overall consistency (see Vue for an example of life cycle hooks done better, even if I have plenty of other gripes with Vue's design)
The vDom was fast enough that it made re-rendering your view on every state change possible.
Now you don't have to worry about rendering, you describe your components as pure functions and you're done with that side of things, now you "only" have to worry about the state of your app.
Seeing comments here wondering "How am I supposed to use jQuery with React otherwise?" is baffling and disheartening.
React isn't low level enough to be this unopinionated.
This seems to be especially bad for anyone who originally worked with class components. The mental modal shift was large and people tried to fit their understanding of lifecycle methods into hooks, attempting to replicate similar behaviour. The docs at the time did a poor job of explaining why this was a bad idea.
Seeing this reminds of the time I was spending to understand these concept and use them. Quite funny that the whole React ecosystem is turning into a classic server rendered mechanism with core functionalities being deprecated. I'm glad I stayed away.
I'm inclined to think Vue is the better framework, but React has a larger ecosystem.
(Not intending to start a flame war here)
Before React two-way-binding of data, known today as Signals, was the way we did things. Angular is probably the best example from the pre-react time of that.
React came along with a philosophy of one-way-binding also known as unidirectional data flow. I’ve stuck with it because I think it enables much better local reasoning, and more predictable outcomes.
Neither are outdated.
I’d wager the most performance critical use cases would benefit from signals, things like tldraw.
But unless you’re doing something which needs to paint every single frame (ignoring animation/transitions), the extra overhead and mental load required by Signals is excessive. It’s overly complex and harder to reason about.
With no disrespect to Vue, Svelte, Solid, etc, they feel outdated in the way Angular does.
[1] https://vuejs.github.io/vetur/guide/vti.html [2] https://vuejs.org/guide/extras/render-function.html [3] https://vuejs.org/guide/extras/render-function.html#function...
Is typing really the main issue there? Working in render functions and TSX is sooooo much more clunky for doing the same basic things, and you lose all the nice benefits of Vue's SFCs and things like built-in class array logic.
Many useEffects can be replaced with event handlers and calculated constant variables
The response to this has been absurd. Dan Abramov has been active in the community and transparent about the documentation, and he has gotten direct ad hominem attacks from the same community.
It's just the same story: People that feel lost using React claiming the rest of tech world is fed up with it, and it will be dead next year. I question whether they even do frontend work that much, or are really backend devs forced to do it once in a blue moon. Meanwhile every other library/framework trying to "outdo" react just falls short in so many areas. I welcome the competition, but it's clear that these aren't the members of the community that should be tastemakers for frontend design.
Later on I got a job in react with typescript and serverless functions and it’s a dream.
There's nothing here that's specific to hooks. It's just people misunderstanding the React lifecycle.
- the function is run immediately (during the render cycle, not after)
- we can take advantage of the dependency array
I think the reason this pattern isn't more popular is that we're taught to use useMemo to memoize a result, but we can also use it more simply … to run a function (during the render cycle) whenever the dependency array changes.
Just call useMemo and don't assign the result to anything, such as:
useMemo(myFunction, [dependency array])
Many applications suffer from the waterfall rendering problem but even for those that don't, if they are large, they will absolutely want to kick off network calls on "the way down". You can easily add a ton of time to LCP by waiting to kick network calls off until the tree has mounted.
I actually go a little out of my way to make sure as much of the tree as possible can render and mount with missing data. This way the work is occurring while data is fetching in parallel, and then a minimized number of components need to be updated once the data has arrived.
You can confirm this via console.log:
useMemo(() => console.log(1), [])
console.log(2)
… will print 1 then 2.
If you replace useMemo with useLayoutEffect, you’ll get 2 then 1.
Whenever you want a stateful component that can be identified by a unique name, you’ll likely want to tell that to React via key.
Use state as little as possible and keep things pure and you probably won’t need this.
Example: date picker only keeps the open/close ui state of the picker. The date is a prop and date change is an event.
Instead of original date being a prop. Current date being local state.
This doc is generally on point and a very important lesson to drill into the heads of people new to react, however that particular phrase is completely misleading. React is absolutely pushing you to use lifecycle effects where necessary. Calling that "stepping outside" is just dishonest.
React is not a "pure view library" that you have to integrate with state management. It makes you handle the extremely impure chain of (route change, component "mounting", data fetching...), all in the rendering code.
This should really be:
route change -> data fetching -> rendering
After the data is fetched, it should just be assigned to a state somewhere, and the React should, well, "react" to the state change. This may seem a bit complicated for simple cases, but keeps the execution flow explicit and "steppable" though the debugger.
We use MobX for the "react" part; I'm sure this is not the only way.
https://react.dev/reference/react/useEffect#what-are-good-al...
I don’t think it’s dishonest. There’s two main phases in react, render and commit.
Render is interruptible. For example when a new event like an interaction comes in, the in progress render might get chucked out and restarted with the new information.
Commit isn’t interruptible. It’s a sync phase. This is where all the effects run, and the purpose is to take the new state from the render phase and synchronise it to the external systems. Aka cause side effects.
The useEffect hook running in the commit phase, does let you step outside of the react framework. It lets you safely interact with external systems. If you tried to do any of that in the render phase, you risk getting cut off halfway, or doing things out of order.
> React is not a "pure view library" that you have to integrate with state management.
A view library without state management would be a templating engine right?
> It makes you handle the extremely impure chain of (route change, component "mounting", data fetching...), all in the rendering code.
It lets you define how to respond to those changes in the render phase, but they aren’t applied until the commit phase.
And that means you can wrap up the complexity in well tested components that provide a simple API.
What if I'd like to always initialize my app's state with the data in local storage? Let's say I want to initialize from localStorage and pass this to context. Doing so triggers a hydration error in React v18 because the server-side value will never match the hydrated value once set. It seems like a job for useSyncExternalStore since that's what localStorage is (an external key-value store), but the ergonomics of the subscribe function seem odd to me.
Having to keep a second variable around for every prop you need to compare is just miserable to work with. useEffect is less efficient, but so much more obvious in a situation like this. "I want to do something when this value changes, and ONLY when this value changes" seems like something React should be able to handle elegantly. It happens literally all the time.
ducks
It makes no sense at all.
A quick glance at the Elm Discourse or Elm Slack will surface that our community and culture is alive, well, and growing.
Evan continues to quietly work on vnext, and is presenting at Goto Aarhus in May.
I'm not interested in changing your mind in particular, and I do not wish to argue; I am merely posting this for the benefit of anyone that should see your comment.
Sometimes, shipping fast and bouncing approaches all over the place is not, as it happens, the most sane and sustainable way to create lasting software. You and others can continue along doing that; we will continue to ship our hundreds-of-thousands-of-lines-of-code Elm frontend.
I guess this makes sense, but it's weird after all the "never do anything stateful in render"
This should be faster than useEffect which only executes after the entire render has been committed to the DOM.
This useful guide points it out as well: https://blog.isquaredsoftware.com/2020/05/blogged-answers-a-...
Both do away with much of the complexity of React.
Elm has been going nowhere for ages now, and will continue to go nowhere, as long as they keep not listening and don't give people what people ask for.
Given how common this error is, there is definitely an unmet need. What people want is a listener, ie.: - recalculate a var whenever a prop changes (this is what useMemo is for) - trigger a function when a state changes (should be moved to the function that changed the state)
Feel free to refer to the HN rules on comment quality.
Maybe I’m just being pedantic here, but I think how you use words matters, and in this instance a better title would be “avoid unnecessary useEffect calls” or something like that. “You might not need an effect” is just poor (sloppy?) wording, because it’s usually objectively wrong.
The reason it matters in this instance is that with the current title I’m going into the article thinking I’ll learn some new way to do effects which is better than useEffect. But the article offers no alternative to useEffect. You will need useEffect.
I think the context is “for this use case”. Neither are saying never use Redux or useEffect, but are more about the common problem of people reaching for solutions that aren’t a good fit for their problem.
When I have seen useEffect used, it's most often uneccesary. People are using it to create dependent state (ie. when x changes, then recalculate y) or to handle the result of an event.
It's really only needed in components that handle fetch responses, which IME is much fewer than 99.9%.
Everything else is triggered by event handlers. I work on a 70Loc react app and useEffect is used maybe once outside of this context. You really don't need it.
And I don't know what you mean by "listening to browser events" with useEffect. You really just need to use onClick and onChange.
You also don't need it to set state, you need calculated constant assignments.
// Good: This logic should run because the component was displayed
useEffect(() => {
post('/analytics/event', { eventName: 'visit_form' });
}, []);
And then a wild 'exhaustive-deps'[0] appears!well I’m lost then.
glad they’re writing this up. I come to react after hooks was introduced, and have been kind of put on a pedestal for not needing to “learn hooks” since thats all I know, for React.
still looks like I need to relearn something.
there are a ton of tutorials out there that teach new react devs the wrong patterns. really low bar of entry to put some article up on medium or dev.to about "useEffect tutorial". and then those devs go on to write their own tutorials that are misguided, etc
It is now useStrongEffect()
Even useLayoutEffect goes on an effect queue, and if it results in changes, will need to render a second time, while a changed memo calculation happens entirely during the single render pass.