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?
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.
Heh, we already have that and it is called Elm.
By me, an Elm user until 0.18.
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: https://twitter.com/SisNode/status/1458942812087414797
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...
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'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...
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.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 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.
"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.
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.