I've been using I think the beta version of this recently, it's definitely an improvement and makes the react api seem a lot more coherent than the old version.
I'm glad they fixed the janky scrolling though that has been cracking me up for a while. It's an example of a common complaint/pitfall with react and even the official docs were plagued by it. As a heavy react user and light react hater I love to see that shit lol.
Please don't hesitate to report things like janky scrolling to the issue tracker (https://github.com/reactjs/reactjs.org/issues). We were changing things a lot and I'm sure a bunch of regressions could've crept in, so all reports are super helpful.
Is basically useless when scrolling. All I see is a gray background, and when I stop the content disappears. Makes it hard to quickly skim to the correct section.
Looks really nice! Also, super happy that the interactive examples are not super sluggish code sandbox iframes, they're actually usable! A more typescript-first approach would have been nice, or at least typescript/JavaScript toggles, but the React team seems to be aware that that's an area they need to work on.
That is something I really dislike about the react-query docs, the examples are code sandboxes. Glad to see they didn't go that path here. I find they add a lot of friction when I am looking for a quick reference.
Looks like some good resources and a neat redesign. The domain move makes me realise how odd it was that before they had 'org', which is normally used by charities/non-profits. I'm guessing Typescript is the force behind dropping 'js' from out of the domain too.
As the person who most often pushed for .org, I can say it was because I wanted to emphasize the community nature. React isn’t a commercial offering. (I had to talk the team out of react.com as the main brand for this launch; although that URL works, I think react.dev is more reflective than .com of what we’re trying to do.)
Finally! Although looks like Google still has to update so newcomers are likely still going to head to the legacy docs and not realize it (and the legacy page still links to beta.reactjs.org vs react.dev).
A tremendous improvement though, and it must have been a lot of work coordinating, especially the easier to digest images sprinkled throughout.
At last we can officially move past the era of class components.
We'll make the legacy site binner a bit more prominent and clear -- thanks for feedback. I planned to but got distracted with other things during deployment.
Perfectly understandable, it's a huge change and these smaller things fly under the radar easily. Congrats to you and the team for pulling off the major transition smoothly!
Are class components really on the down & out? I tried going all in on hooks with a complicated app using lots of web workers and generally heavy computation and complex logic (a game). It turned in to a bit of a mess until I went for a refactor into classes and everything became much more clear.
Having a dedicated place to process changes in props and setup/teardown logic is just generally nicer than weird dependency arrays and returning destructors from effects.
Most React codebases benefit from the simplicity of functional components, because they fit into the model most people have of websites + web apps. If you’re doing anything complex, you’re in a very small group.
I would argue the inverse: if your project relatively simple enough that hooks don't feel messy, then it probably doesn't need to be a React app to begin with.
Class components are just (IMO) cleaner, and I find myself saddened at the level of disarray most codebases with hooks are nowadays. Enough has been written elsewhere about how hooks require one to keep more in their head; class components have an agreed up layout/structure/etc that is important in large codebases.
The most frustrating part of this is when you run into some library that is all in on hooks and insists you use hooks, but your project has been using class-based components so far. I was having a great time making a game prototype with react-three-fiber until some model loading code was just like "fuck you, use hooks" and I ended up wrapping a bunch of functional components in special ways to basically isolate the hooks stuff so I don't have to deal with the headache of it all gradually encroaching on the rest of my perfectly functioning project. It's frustrating because every time I wonder how to make it not a mess, the answer is "haha, we lied, they're barely supported; class-based components are for losers and you should feel bad."
It's just frustrating. Couldn't they have had the decency to fork / rename React for all this and leave the old branch to die instead of turning the entire space into some kind of pedantic war-zone for several years?
I think generally you should start with functional components, try to keep things simple, and use class components as the exception and not the rule. My own app is mostly functional components, but I did fall back on a class component in a case where I needed the lifted state of a function reference to remain current across renders.
One pattern you might like to try in this kind of code is writing your heavy imperative logic in classes (not React class components! just normal classes), and then using Hooks very lightly to "connect" and "disconnect" things.
Oh wow didn't expect to hear form you! Yes that's what I'm in the middle of right now actually to enable online multiplayer: everything regarding the game is in a (class-based) state machine in pure JS that can run frontend or backend and be interacted with via a websocket client (this actually helps with a11y too, navigating a board game via aria-labels was a pain in the butt), and the UI (still WIP) will be far thinner. Perhaps I'll give hooks a try again for that.
Because you have to write more code. If all you have is a single variable that gets updated with no complex logic then writing a reducer seems excessively boilerplatey.
One example of this pattern is Searchkit [0] which performs most of its logic inside a singleton Searchkit class which is instantiated and passed as a prop to the root React component. A bonus is that it's easier to implement bindings for Angular, Svelte, etc. since they can rely mostly on the class and just need to call the appropriate methods exposed on its interface. For example, it looks like Searchkit now suggests using InstantSearch (react-instantsearch-dom) [1] from Algolia, i.e. an entirely different maintainer, and it creates the bindings with a `Client(new SearchKit(...))` adapter [2] around the class (see the code on the home page at [0]).
There are downsides to this - you do need to think a lot more about code architecture before implementing the code, and you can get into a really messy/unmaintainable situation if you aren't careful. And you'll need to consider what state your "singleton" is tied to - is it really one per page, one per component, etc? And depending on the answer, the implications may be that this pattern isn't a great idea. But for certain use cases, it can work well.
The core premise of this pattern is separating business and UI logic, which is not exactly a new idea.
I joined my current company around the time hooks reached peak hype, and whilst the team I joined were refactoring away from class components. My previous gig had used classes. My current codebase is complex (a video player) but pushed all the core logic out to Rx and custom code, rather than going deep with hooks and react ecosystem. So I’m still way more familiar with classes despite ostensibly working in a hook-y codebase.
I hate hooks. The syntax feels nice to type, but the issues outweigh the benefits for me. It’s way too difficult to understand the rendering lifecycle, the state updating, the ordering and I also find the reuse abstraction hard to follow (although I accept that might be a concentration/attention issue on my part). Conversely, it’s also way too easy to break the purity of the hook callbacks, so hooks can use state from contexts you wouldn’t expect (more an issue with custom hooks if you don’t also supply linter tooling to go with them).
I like them too but they tend to promote shittier code. The codebases I inherit that use classes = clean, easy to follow. Ones all in with functions and React hooks = trash.
Obviously using hooks doesn't mean it has to be shitty, I think it just brought a lot more wannabe React devs that have no experience with architecture or maintainability.
I have had a similar experience, and feel the same way.
I tried to give it a real shot with hooks, but the marginal benefit they bring seems to be far outweighed by the added complexity. Add to that the mixing of paradigms with some functional components, some class-based, etc and it becomes a mess pretty quickly.
The hook system is the parts of an OO system that React needed to be able to keep developing features & optimizations for function-based components, rather than telling their users & devs that they'd simply have to use classes for some things. It exists so they could side-line class components, rather than having to become outright reliant on them for some features. So, pretty much, yes.
One thing I find strange about the new documentation is how React.createElement is considered a "legacy API". Doesn't JSX transpile into calls to that function anyway?
This worries me a bit because some React wrappers for ClojureScript expose macros that essentially compile to React.createElement() calls, which are now labelled as a legacy API.
It also looks like Class Components are officially deprecated, given that the documentation explicitly states they are not recommended for use in new code.
JSX has not been compiling to createElement() for a while. (If you have the modern transform enabled.)
We're not removing createElement but I'd recommend to change your wrappers to the same compile output that the new JSX transform (introduced in 2020) uses: https://legacy.reactjs.org/blog/2020/09/22/introducing-the-n.... The new JSX compile target will allow us to do a bunch of optimizations in the future that createElement() can't.
In our embedded/plugin component scenario where we are given a <div> to load in, it appears we should replace our current pattern ReactDOM.render(React.createElement(... with createRoot(_jsx(....
ReactDOM.render has been deprecated since React 18. If you're running React 18 with ReactDOM.render, you should already see an error urging you to switch because essentially ReactDOM.render works in React 17 mode.
Is there an explanation somewhere on react.dev covering how Hooks actually work under the hood? They're the most magical part of React, and everybody I know (including me) had a hard time actually grasping how they work and why, for example, you can't use a hook inside an if branch.
Edit: there are a lot of good--and varied--explanations here. Which is why I think the docs should cover it in-depth. It's confusing.
My high level understanding of how they work is that they remember the sequence in which they were called - so if you call the same hook three times, those three calls are held in a list - and future actions that should retrieve them get access to the correct data.
If you were to call them in an "if" branch it would mess up that queue mechanism.
1. They work by setting a global variable to the value of the current component then calling the render function. Whenever you call a hook you're effectivelly dispatching it to the component in question, OOP style.
2. React counts the number of executions of (certain) hooks. This count is how it knows which state to get from the store as a return value from `useState`. useState is effectively `getStateAndSetter()` but it doesn't pass a key name of any kind, so the implicitly passed key is `hookCount++`. This is why you can't call hooks conditionally, or state would get all messed up - if a condition turns false and one hook doesn't run that render, all getStateAndSetter calls that run after it will be off by one.
Here's how I'm pretty sure they work (although I haven't actually looked at the internals). Since your `<Tagname props>children</Tagname>` gets turned into `React.createElement(Tagname, props, children)`, this means your `Tagname` function isn't called directly. So before it calls that function, it sets up a "context" that the hooks will use during that function call (with a global variable pointing to that context so the hooks can find it). We could use an array with one element for each hook invocation. So each useState would use a different slot in that array, etc. This is also why the order and number of hooks must always be the same for a given function, since their identifier is their index in that array.
Additionally, this context would also have the context of children elements, so things can actually be persisted across function calls and React can know what needs to be mounted & unmounted.
Also note that because Tagname isn't called directly, it's also how React is able to do its diff and only actually call what is needed.
This is also why if you're generating a dynamic number of elements (ie outputting an array of elements), you should provide a `key` prop, so it can link up the elements to their corresponding past states each time the function runs.
However, _conceptually_ I'd recommend to think of Hook return values similar to "extra inputs" to your function, kind of like extra arguments. There are different ways to formalize it in different languages. We picked plain function calls for simplicity and low overhead, although you could imagine `yield` with generators or something like that.
Basically the way of thinking about is that there is a runtime that knows what component is rendering, and your hooks are communicating with that global runtime. This is why hook order and consistency matters - there is basically something globally that identifies a hook by its index order of execution and the identity of the component instance that is currently rendering.
So there is a data structure that store says `[useMemo, useState, useEffeect]` - and when you component re-renders, is unmounting, or has effects to trigger it uses the index order to lookup the bit of state that needed to persist.
I read the code when they added them. It may have changed, but here it is:
1) A hook adds a function or variable to one of several lists attached to the component object, when it's instantiated (yes, even your "functional" components are objects, and I don't just mean in the JS-functions-are-actually-objects sense—at least, they were when I read it)
2) Subsequent calls either call those functions, or accesses/modifies the value, in a FIFO manner, reading them out of those lists. This is why you can't mess with hook ordering in e.g. loops.
It's basically just methods and properties on an object, but with FIFO access based on declaration order, instead of using a lookup table of some sort.
[EDIT] A poster correctly pointed out (then deleted their post) that I wrote "loops" where I meant "conditionals". Technically sorta-true (though not quite, as phrased) if the loop isn't the same length every time, but yeah, I meant conditionals. Point is, the reason order matters is that the whole thing's just a bunch of FIFO queues, more or less.
Everytime I try to understand how something in react works I actually look into how it's implemented in preact. Hooks is around 500 LOC. https://github.com/preactjs/preact/blob/master/hooks/src/ind...
React does basically the same, just in a more complex way (because fiber and native etc). But just giving you a mind model the preact implementation is enough.
Tell me, if React is so great, why isn't Draft.js blowing ProseMirror out of the water? And why aren't difficult parts of VSCode written with it?
I think a good example of where React sits is Deno. The devs who are working on Deno don't seem to have much interest in React, but they are pushing a React framework, Fresh, to make it popular with regular devs. They see the popularity of React and not those who are frustrated with it. One thing is how much typical React code relies on a build step and a bunch of Provider objects. CSS Variables can help make components customizable without having to do CSS in JS.
I think Web Components, maybe with Lit or Svelte, are making more sense for beginning devs. With those you don't have to worry that you might need to work on non-react code sometime.
> I think Web Components, maybe with Lit or Svelte, are making more sense for beginning dev
Beginner devs want to get a job, so they should probably spend their time learning the framework that dominates the ecosystem. Lit and Svelte are cool, but I don't think they're a great target for a first time web developer. Svelte maybe. But definitely not Lit - it's a relatively new library and a moving target without a lot of adoption, meaning there is a sparse ecosystem to fall back on, and you'll need to fill in a lot of gaps yourself (both in terms of libraries for common functionality, and docs/stackoverflow answers for telling you how to do things). Experienced devs can read the source and official docs to figure it out, but newbies need more hand holding.
If Deno's own devs avoid anything similar to React hooks except when they're trying to appeal to the beginning devs, perhaps it would be smart for beginning devs to try to do as the senior devs do, not as they say?
The ecosystem of Lit is the web, which has a lot of great stuff like MDN. It lets you simply use what you learn there. No redirection, like React's onChange translating into the input event.
> The ecosystem of Lit is the web, which has a lot of great stuff like MDN
I agree with this, and I mostly empathize with the purity aesthetic that comes with it. But I think in practice, you need to do a lot of work for common operations that might have entire libraries dedicated to them in React. If you're a pro developer obsessed with purism, you probably wouldn't use those libraries anyway. But newbies don't have time to worry about re-inventing the wheel (or at least, we shouldn't encourage them to do that, since it will probably be a pretty shitty wheel).
All that said, I absolutely love Deno, and I think we should encourage new developers to use it, especially since it sidesteps the need for build steps in many cases.
For what it's worth, the site has been developed by different people over time, so the choice to use Tailwind was made by someone else early on. The team working on the site now doesn't feel strongly about it either way — it's sometimes annoying but overall using it feels really nice! And I'd probably say the same about other CSS solutions too.
I think we'll keep it for now. Where it really shines IMO is fast prototyping. But yeah, it's cool.
Docusaurus is a fine project, but we thought it is important to use React very directly — so that we have a good sense of what it feels like to make an app with React.
Kudos to React maintainers for their focus on documentation. A year ago I decided to learn React and the official docs were instrumental in getting me up and running. For context, last time I'd done any serious coding was when I still had to care about IE6 compatibility, so all the modern SPA paradigms had to be picked up from scratch. I spent the afternoon reading the docs and was able to jump right into coding a basic proof-of-concept app that same day. The clarity of writing, the progression of topics, it all hang together very well. Looking forward to the latest iteration here!
Its like 2 weeks since I had to grasp react, fluentui, react hook forms, webpack and a little more when I started using modules and even more when I used typescript.
React clicked really fast, documentation new and old, articles helped get on track really fast.
Wiring that all together and taming CRA (Create-react-app) with react-app-rewired to add stuff to webpack, like adding libraries to behave like modules that are NOT modules, packing all with scripts into single html, understanding where are boundaries between fluentui and react (both new to me), setting up monorepo because I separate reusable components from app itself and libs, applying css which I am bad at and stuff like that took more time... complexity just explodes, but less "mental effort" overall achieved by having streamlined build, reusable stuff etc.
Otherwise I feel that building app with react takes a lot of "mental effort" away, because you develop a component in isolation which feels simple and when you use that component you don't think about implementation details - it is nicely abstracted away and things just work.
Ah, yes, and I'm lucky I got the signal that functional components are way to go and the only place I had to use a small class component when I want to have a component variant for existing class based component from FluentUi where generic types are important and used within `onRenderItem` and various methods:
> class LookupBaseInternal extends BasePicker<ILookupResultProps, ILookupPropsInternal> {}
> There is currently no way to write an error boundary as a function component. However, you don’t have to write the error boundary class yourself. For example, you can use react-error-boundary instead.
After using Vue for a long time, React felt like an unforgiving piece of cr*p. As time went on and Vue was getting its Composition API while kind of moving in the overall direction of React, I decided to try class-based components and didn’t like them that much (this was a couple of years ago). Recently, I went through these new docs and read and applied the stuff I learned, and I can say it was a surprisingly nice experience. I just needed to get friendly with a composition-ish, functional-ish and JSX-ish way of thinking. Alone the thought of HTML-in-JS instead of JS-in-HTML made me crazy back then (it still kinda does). But when you get the hang of building actual user interfaces instead of just websites, it makes a lot of sense. If you’re comfortable with javascript while daring to take the less-opinionated route as opposed to an all-out framework, you'll simply feel the power!
Anyhow… To this day, I still think it’s an unforgiving, crazy beast of a library for a weekend hobbyist developer like me. But I kinda like it now and in big part it’s thanks to hooks, functional components and these new docs. Good job team, and congratulations on finally getting this live!
Interesting, I had an opposite experience. I started out using React when using class based components was a way to go then stopped for quite some time. Years ago I had to use React for something and decided to use the new approach with hooks and it was such a painful experience - it was harder to learn and use, also I introduced many ridiculous rendering bugs. I still feel that component based approach was somewhat easier and more straightforward to get into even if more verbose.
Yes, it can be tricky, and sometimes I too catch myself asking, "why is it designed like it is" with regards to hooks, I won't lie. But, there is something there that makes it attractive to me where it was off-putting before. Alone as a complement to more extensive state management libraries, or other tools intended for larger scale apps, it serves as a nice middle-ground solution. You can build your own bits of logic in quite an accessible way now too. In other words, for me personally I feel that there is something distinct and interesting going on here.
Finally, if you combine it with the fact that it seems to hold on as the industry standard for jobs, and being useful for native mobile development via React Native, it all comes together nicely in my mind. With its huge ecosystem and closeness to plain vanilla JS, I guess it just makes me simply a bit of a better developer and gives me a new perspective on things. That's that.
As someone who worked with both, vue 3 is miles ahead imho. Scoped css, well working two-ways data binding (now being mimicked again with signals after mobx demise) and reactivity, easier to get performance out of the box without the hook madness.
In many ways I do agree with you, but by now all these front-end frameworks and libraries, including their server-side rendered counterparts (like Nuxt, Next, etc.) just keep borrowing things from each other.
Regarding performance, I remember having blocking sluggishness with v-model that shouldn’t have been there back in Vue 2 days.
Also, for example, in Vue 3, my favorite component library Buefy (based on Bulma) is no longer available, and I don't think any of the other ones have (arguably) come so close and extensive to what I want.
Vue 2's transition to Vue 3 (if you want to use the new CLI tools and the Composition API) had to, by definition, fragment the ecosystem in unpleasant ways no matter how carefully and sensibly it was managed by the core teams in charge.
You can see that Vue went down slightly in recent developer surveys, and I suspect this to be among the main reasons. As if Evan himself would be suddenly more focused on his build tool Vite as opposed to Vue itself (of course it's just an illusion).
But I still love Vue because of their amazing docs, and the fact that they let me enter the world of web-development in a relatively accessible manner (this may not have been possible with React, for me personally). Lack of something like scoped styles alone baffles me in React to this day, but it makes kinda sense in the ecosystem it's operating in...
Oh, nice for reminding me, SSR is also better imho on Vue with Nuxt than anything in react world. Next.js has the tendency of monkey patching plenty of APIs, even broke native fetch when uploading anything bigger than 14 kbs.
Why did react (effectively) get rid of class components? Conceptually, a component seems better represented by an object/class than a procedure/function. And aren't classes just really functions under the hood anyways?
> Why did react (effectively) get rid of class components?
Supporting some features for hooks and classes, both, probably would have meant having two implementations in some cases, that might not quite match up as equivalent. More API surface area, more code to test, more combinations and paths to worry about.
> And aren't classes just really functions under the hood anyways?
Other way around, actually: "functional" components end up represented by an object.
>Conceptually, a component seems better represented by an object/class than a procedure/function.
In other paradigms, it is! Our paradigm is exploring the functional take. I agree it's a bit unorthodox but we are very intentional about modeling it that way. It really has a bunch of powerful properties one might not expect.
>And aren't classes just really functions under the hood anyways?
The key difference is that in React, UI is a pure projection of current data (props/state). You're always supposed to "return" the UI. Sure a class is a function, but that function is invoked once. Its methods can be called many times, but having a pure render() method (like in class-based React) is really a class cosplaying as a function. Functions are more honest to what React is trying to be.
> Classes may seem like the ideal thing to hold state since that's what they're designed for. However, React is more written like a declarative function that keeps getting executed over and over to simulate it being reactive. Those two things have an impedence mismatch and that keeps leaking when we think of these as classes.
>Another issue is that classes in JS merge both methods and values on the same namespace. This makes it very hard to make optimizations because sometimes methods behave like static methods and sometimes behave like values that contain functions. The Hooks pattern encourages the use of more statically resolvable calls for helper functions.
>In classes, each method has its own scope. It causes issues like us having to reinvent default props so that we can create a single shared resolved object across those. You also encourage sharing data between those methods using mutable fields on the class since the only shared thing is this. This is also problematic for concurrency.
>Another issue is just that the conceptual mental model for React is just functions calling other functions recursively. There is a lot of value to express it in those terms to help build the correct mental model.
For a concrete example of where classes as a model fails us, consider useTransition (https://react.dev/reference/react/useTransition). It lets you start rendering "in background" with a different state value. But if you get interrupted, the renders have the current value. This highlights that in React, the same piece of state can conceptually be thought of having more than a single value (kind of like being in parallel worlds). Classes don't model that well.
The operative word there is 'exploring'. In practice, hooks are still basically OOP, albeit masquerading as FP. You just shuffled the state into a shadowy realm adjacent to the component where it's harder for developers to see. I know a lot of JS developers are allergic to writing the 'class' keyword (even though JS doesn't really have classes in the first place), and I guess hooks help them sleep easier at night by allowing them to believe that they're writing their code in a functional way. But they usually aren't. And I think that misdirection is the source of a lot of the confusion out there regarding hooks.
"The greatest trick the OOP devil ever pulled was convincing the world that state didn't exist"
I really don't think React is OOP. There is no misdirection here — it really is a different conceptual model.
Even if we set aside implementation inheritance, class hierarchies, and all that jazz that got associated with modern OOP, fundamentally classical OOP is message passing. React components don't pass messages to each other in that sense. The data flows strictly down. Re-rendering is not message passing but conceptually reevaluating a part of a lazy continuously reactive function call tree.
It's not about "sleeping easier at night" etc, it's just a different model. If you're curious to entertain this idea for a bit, I have an article you might enjoy reading: https://overreacted.io/react-as-a-ui-runtime/
I did enjoy that article; I think it's a great overview of React's essential architecture. But I don't think it obviates the point I'm trying to make.
> fundamentally classical OOP is message passing
OOP in its essence is about using conceptual data models as state containers. Cats, trees, input fields. Message passing is just the means by which these containers interoperate. The persistent state is the defining element.
Judged in this light, I'm arguing that the way most people write React components is more OOP than functional. The components maintain some state inherent to their nature. And I would argue that these components are still passed messages, or at least one: "render". When a component is passed the "render" message, it uses its internal state to output the appropriate DOM structure. A news widget maintains the list of headlines to display. An expanding menu holds the state for which menu items have been expanded. A form holds the errors that have been triggered from input field validation. And so on.
Thus, each component is still essentially a polymorphic implementation of an interface with a "render" method: an "instance" of a "class" that encapsulates state, even if the actual language-level class doesn't exist anymore. The mechanics are essentially the same. Since "render" was the only method being invoked on the component class, you took that lone method and promoted it to a standalone function that is invoked directly. And then you took the internal state of that class, and moved it behind the hooks API. That way, the "render" method can still access the class's internal state despite the absence of the "this" keyword. That's what I meant by misdirection. The class has just become virtualized.
Obviously it's possible for people to write React code in a way that avoids local component state. Naively we could just do prop drilling, in which case your argument that "the data flows strictly down" would actually be true (as opposed to being commingled with the data that is being stored in local component state at various levels). Or we could use Redux components, which, when written "correctly", are essentially pure functions that accept the entire application state as an argument. Et cetera.
Maybe that's actually your philosophical vision for how people should be writing React code, in a frictionless-plane-of-Platonic-ideals sort of way. Personally, mine is pretty close to the Redux model: components are just a Pachinko machine of nested functions that emit UI, and my application state lives outside of that machine, and is fed to it as an input.
But that's not how most people actually structure their React code. Instead, they continue to model their application as an aggregation of conceptual objects a la OOP. Forms. Widgets. Color pickers and the like. Each with its own persisted state. The fact that you've architected React to avoid semantic classes is orthogonal to the fact that people are still using it to implement conceptual classes.
> It really has a bunch of powerful properties one might not expect.
Can you elaborate on this?
> a class is a function, but that function is invoked once. Its methods can be called many times, but having a pure render() method (like in class-based React) is really a class cosplaying as a function. Functions are more honest to what React is trying to be
useState() is a function cosplaying as a class ;) and that's really my biggest hangup. It seems like react components are not quite functions nor are they classes but rather somewhere in between... And because of JS quirks and perhaps the internal architecture/mental model, functions end up being a better choice.
> in React, the same piece of state can conceptually be thought of having more than a single value (kind of like being in parallel worlds). Classes don't model that well
Maybe I'm missing something but this seems like a false dichotomy because the equivalent in the class-based model would be the render function, not the class itself. And in that sense, your set of render functions can also have multiple values given a state. Though just writing this out does make functions feel a bit simpler.
Admittedly, you've mostly convinced me! And after reading through the new documentation, going function-only feels a lot cleaner than it did when I learned react using the old docs.
Unbelievable that they’ve gone ahead launching this, going all in on hooks, while the fundamental problem with them, which was raised in 2018 is still unsolved: https://github.com/facebook/react/issues/14099
And they just handwave it away in the docs with an imaginary future API. Embarrassing.
The quick version is we're looking at this from two different sides:
- To avoid re-triggering stuff from Effects, we _are_ adding a Hook. It has the same API as the original `useEvent` proposal but is more tightly scoped to this particular use case (and different semantics). We'll submit a new RFC for it after some more testing, but it's available (as `useEffectEvent`) in experimental builds, and the docs mention it: https://react.dev/learn/separating-events-from-effects#decla...
- To improve rendering performance, we are working on an automatic compiler that analyzes your code and makes rendering much more granular. It's still in active R&D but we hope to share an update on it soon.
We think these two things together will likely be able to mostly address the issue. If not, we'll look at the specific gaps and work on them more closely.
In general though, people have shipped huge apps with Hooks, and it's definitely production-ready. Always an opportunity for improvement, sure.
Yeah, I’ve helped build some of those huge apps, and I’ve ran in these issues and wasted countless hours debugging and working around them in all of them.
And what do we get from the React team? After five years? More vague promises. More RFCs. More blog posts. More tweets. More docs mentioning nonexistent APIs. No actual shipped solutions. Forgive me, but I just don’t believe you anymore at this point. Five years.
You’re obviously a bit frustrated but as someone who has done a lot of React over the years I don’t quite get what you’re running into that would make you that upset.
Can you be a bit more specific about which issues you've ran into? That would help me know whether we're addressing them or not. (The linked issue is very broad so it's not super clear which part of it you're referring to. We think it conflates a few unrelated things.)
I think it's fair criticism we've been slow on shipping this Hook. We try to take a very cautious approach to introducing new APIs, so we are currently testing it in our internal codebase. Once we feel confident the approach makes sense, we will make a stable release with it.
There’s always a lot of complaining about hooks but honestly I think they’re generally pretty wonderful. Occasionally you need to do some gymnastics but more recently I’ve lent on the useEvent pattern to detach reactive and non-reactive code and it’s working nicely.
Is there something I should know about useEffectEvent vs useEvent? We use the pretend / poly fill version and I understand it behaves slightly differently. But in general we just want a function that doesn’t change that always calls the latest version of the real function. Mostly we’re passing it down to other components that use it as an event handler or sometimes in a useEffect themselves.
>Is there something I should know about useEffectEvent vs useEvent?
Yea. It still proxies to the latest version, but it doesn't give you a stable function. (In fact, it always gives you a new one in DEV to avoid depending on its identity.) Instead, the _linter_ lets you (actually, forces you) to omit it from dependencies. Conceptually, it's a non-reactive piece of code. There's also a new limitation that you're supposed to only call it but not pass around (also enforced by the linter). There's a bunch of reasons for this design which we'll write up in the RFC. (TLDR: you only really know whether something should be reactive or not next to the actual callsite.)
This is certainly an issue/annoyance (along with other things I don't care for about React), but calling it a "fundamental problem" and "embarrassing" ignores the vast, vast majority of React users who manage just fine.
This may come across as naive, but could you expand on the significance of this issue for people who don't use React every day? I see that lots of people think this way about Hooks, so I'm honestly just curious about what React users think is such a big deal
its not naive. this isn't a problem you're likely to run into or have to work around. It is a thing that pedantic people bring up to justify the trouble they have keeping up with the pace of change in front end dev.
Can you elaborate on what this issue prevents you from doing? I write production react code that is used in medical devices every single day and I have never had this problem stop me from developing a particular screen or piece of functionality, and I use pretty much every niche edge web API that there is.
Hooks has made me 3-4x more productive. If a technicality is stopping you from enjoying it, that is such a shame.
The world has embraced hooks, for better or worse. The fact is React still works well, and the React team has always been clear that performance is (somewhat) an implementation details. For example, they often advertise to use inlined functions and to use `useCallback` only if you're facing performance issues.
So `useCallback` invalidation is definitely not "fundamental" (imho)
What are you talking about? Hooks are fine and are out there working fine for everybody. They have some rough edges but it's not, like, some catastrophe.
And yet, entire startups have been created and acquired during that time, many using React and many using hooks. It appears this "fundamental problem" is far from a show-stopper. Ultimately, that's why people like React: it gets the job done. The API is small and extremely stable. The last major breaking change was hooks, and that wasn't really even a breaking change - just a new paradigm that you should move to eventually (class components do still technically work!)
Honestly, by the time class components are fully deprecated, you'll probably be able to ask ChatGPT to rewrite your codebase to use hooks instead...
Credit to the React team. The docs prior to this were poor - I believe the team admitted this.
From the style of writing, to the style of site, I can tell a lot of effort was put into this. Will check out react this weekend - it's been a couple of years (we've been using svelte)
I recently went through the entire beta React docs (took me about 3 weeks) and was blown away by the quality of the tutorials. The live code playgrounds and challenges are amazing. With the recent debate about reactivity and which framework implements it best (solid vs. react vs. vue vs. svelte), I felt that what mattered most to me as a junior dev is the quality of the documentation. I gained a strong enough understanding of the complicated concepts (dependency arrays and the useEffect hook) because the docs were so good. There is always room for improvement in JS land and other frameworks are constantly innovating, but the thing I always fallback to is: how good is the documentation?
Side note: I recently took an assessment test on React through Triplebyte and was given an expert level badge (for what it's worth). I think that speaks volumes about the new React docs because that is where I gained most of my React related knowledge.
IMO React is missing hooks to work with promises. I made that hook myself but I think that promises along with AbortSignals (to interrupt promise if React decided to cancel it) are basic JS API and React should just support them out of the box. I saw too much code which deals with async functions in useEffect in a buggy way. Things like ReactQuery are nice but they should not be required.
Well, bummer. I have a mature product using React Components which are now legacy. It looks like in the future, I'll slowly migrate these over to functional components, as is standard in the documentation.
I'm disappointed by the fanatical adoption of hooks, but I saw it coming and I can't say their legacy documentation didn't warn me.
I'm happy that other people seem to enjoy them without restraint, but obscuring magical details and making side effects seem like more of a big deal than they really are in programming seems like a design choice intended to infantilize engineers and shelter them from reality.
I might finally invest some time into what it looks like to create front ends independent of any of the existing frameworks that exist today, which I think is probably controversial, but I want the decisions I make to last longer than the whimsy of engineering teams who don't care that they might change their mind in 10 years.
I think having seen front-end software come and go so many times, I'd rather write some simple utility functions wrapping `Document.createElement()` and use native event handling.
Too much fluff in front-end.
I want the decisions I make to last decades, not just a few years. I don't think that's a sentiment appreciated by most, though.
:) True. My first web development experiences were on AOL Hometown and reading people's 1337 scripts from dynamicdrive.com. DHTML! Updating PHP scripts over FTP! Shared hosting.
I worked with class components first. Built a whole app in them. Now we’ve migrated to functional and I look back at the class based ones wondering how I ever dealt with that.
I didn’t want to adopt functional, but let everyone make their own decisions on what to use, and now I’m very happy I did.
> I want the decisions I make to last decades, not just a few years. I don't think that's a sentiment appreciated by most, though.
Asking for an API interface to be stable for decades __across all future versions of the library__ doesn't sound realistic to me. Nothing is forcing you to update to the future new version of react - which has not been released, and is likely years away - that removes support for class components.
I've spent the vast majority of my career outside of front-end development. You're being extremely condescending and presumptuous so I will not engage further.
For a lot of Linux distributions, you have a problem and you go online to find a solution. Depending on how old the stackoverflow (or w/e) answer is - the answers you find are out of date. So you find yourself sifting through a pile of possible answers and you need to figure out which answer applies in this context.
Compare that to FreeBSD where it feels like every effort has been made to stay backwards compatible with the legacy APIs for decades. I found a web-book on FreeBSD network administration once and almost dismissed it because it was last updated in the 90s but, upon closer inspection, most of the book was still relevant and I could administer a FreeBSD network using the same commands the book was using!
As an industry I do think we devalue tribal knowledge and the accumulated ecosystem of documentation that gets invalidated with every breaking change. Every time you ship a breaking change all of those hard learned lessons across all of your users that are sitting in the back of their gray matter, in their blogs, and in stack overflow answers all become invalid - at best they get removed/updated and at worst they stick around and mislead future adventures.
This is exactly what I'm doing with my personal "framework" I use for client contracts. It's just Web Components with a handy class based wrapper. I call it Template, since it's based off <template> tags.
It's a joy to work in, feels "frameworky" but it's just web standards with <100 lines of convenience JS wrapped around it. There is no magic beyond what the browser provides - I like it that way.
It's "open source" as a reference. Just using it for myself. There aren't many docs beyond notes to myself. But the actual framework is a 90LoC JavaScript file that is an easy read.
You're welcome to kick the tires. If you like it I'd entertain PRs and stuff but it's such a small library forking is probably entirely reasonable to make your own flavor too.
The general idea is you extend the Template base class and call "super" with the id of the <template> that will get bound to the ShadowDOM when the class is mounted. Then you call instance.mount and pass a dom node to mount it into the DOM. For child nodes, you use `this.fragment.querySelector` to select them from the <template> you mounted. It supports garbage collection by tracking children, so when you "unmount" it recursively unmounts all child instances as well. Finally it has an event emitter implementation, so changes/actions/events bubble up the DOM while state can push down through the DOM. Keeps things clean.
I recently added state methods since I was duplicating state management everywhere. Now the base template class has a `setState` that will emit a single `change` event for all changes in the current "tick" of the browser eval loop.
I like how for your brief documentation you start with a single index.html file. Too many web/framework docs start with a command that gives you a directory full of who knows what, which tends to negatively affect beginners more than anyone
Glad you like it! Feel free to take it and make it your own.
If you make changes, I'd appreciate a follow up GitHub comment that lets me know where you made improvements, but no obligation. It's licensed under a BSD-style license so you're free to do with it whatever you like!
> seems like a design choice intended to infantilize engineers and shelter them from reality.
The entire front-end framework landscape is like this. It's all designed to appeal to the kind of "engineer" that just wants to copy paste code and have it work like magic without ever thinking about what's actually going on.
This is exactly the “just use react” stereotype. Download a random react component from NPM and 90% chance they just added wrapper divs until they could get the CSS to (kind of) work.
It's just front-end engineering in general, you don't get this with people who work in other subfields of software engineering. I get it, though. It's OK until you need to learn and grow beyond it, which I think anyone doing this long enough will.
It sucks that people learn this the hard way, though, and the only way to grow beyond this for people new to the field is to eventually get burned and learn there are other ways to do all of this.
There are whole generations who don't know what session cookies are! And... that's just OK.
Let’s better spend time on important things instead of wasting time trying to make UI behave properly across a plethora of browsers, OSes and platforms.
> instead of wasting time trying to make UI behave properly across a plethora of browsers, OSes and platforms
Cross-browser compatibility was the problem that jquery was trying to solve back in 2008 or thereabouts. Not a problem that react is trying to solve in 2023.
My biggest gripe with them as a technology is that they have no way to associate a `<style>` tag in `<head>` with a ShadowDOM tree (short of cloning and injecting w/ JS).
The only way to say "this CSS goes with this <template>" is to nest the `<style>` tag in `<template>` itself or painstakingly expose `part`s on everything you want to style.
I'd really like a template selector for CSS where I can say something like `::template(#id) button` to select all buttons inside the `<template id="id">` for whatever ShadowDOM it is mounted to.
Without this, I either have to do CSS in JS, CSS in HTML, or I have to use special build magic to take my standard index.css files and inject them into my HTML or JS. All of that is kinda gross.
I'm slowly trying to get most of my projects and client ones to leverage more and more web components, I'm sick of reimplementing the same things over and over.
> seems like a design choice intended to infantilize engineers and shelter them from reality
I think it's more charitable to assume that the designers are designing this for themselves and are just being aware of their own fallibility and limited mental capacity (we're only human after all), not seeking to infantilize or shelter some lesser class of programmers.
Eh... having worked with a Facebook developer tools team, they quite literally do dismiss the vast majority of the engineers at the company as "those guys who wouldn't be able to understand the stuff us devtools people do".
Your will be surprised how nicely a functional approach with hooks can improve your frontend architecture. Side effects are the source of all evil, it‘s quite useful to take special care.
I think the thing is that you're focused on this specific implementation detail of React. I just don't care. Not that you're not right, either! It's just a matter of competing interests.
I'm running two businesses with a third on the way. I have bigger problems than people reinventing stuff with no benefits compared to what we were doing 20 years ago.
I'm writing software every day, so I'm still involved in front-line work, but I'm interested in being the level of productive most people aren't.
So the bigger questions on my mind are things like, "What knowledge can I build up that doesn't become obsolete?" "What social effects drive adoption that endanger those goals?"
Those are questions that have, at least immediately, very little to do with implementation details, and are questions that help me navigate whether to ignore new technologies all together, or when I identify something new when I decide to adopt it.
A part of that is looking for cues from maintainers that say, "Hey, we care that you're shipping, and we're not going to endanger your labor spent learning our intellectual property."
> I might finally invest some time into what it looks like to create front ends independent of any of the existing frameworks that exist today, which I think is probably controversial
This is a great idea. Nothing controversial about it.
You end up having to answer to people in interview processes that have fewer years of experience than you, and have written sometimes orders of magnitude less code than you, and wonder why you make "weird" decisions not knowing that those decisions have been made with a collection of exceptional experiences.
Lots of engineers out there today who don't have a simple answer for, "So what happens when one of your dependencies no longer exists on the Internet for one reason or another?"
I won't immediately update, since future feature development will take priority, but eventually I will want to update since the current industrial risk is that surrounding dependencies may have bugs that are a risk to customers.
It's a low likelihood, but I'd rather stay up to date when I can.
> Well, bummer. I have a mature product using React Components which are now legacy. It looks like in the future, I'll slowly migrate these over to functional components, as is standard in the documentation.
The writing has been on the wall for 4 years that hooks are the future. You _can still_ use class components. Function component with hooks is the simplest API you can get to React itself—classes are more of an abstraction.
> I'm disappointed by the fanatical adoption of hooks, but I saw it coming and I can't say their legacy documentation didn't warn me.
We all adopted hooks because it makes things easier to reason about. If you’re still having trouble understanding them, I’d urge you to dig deeper into how they and React works.
> I might finally invest some time into what it looks like to create front ends independent of any of the existing frameworks that exist today, which I think is probably controversial, but I want the decisions I make to last longer than the whimsy of engineering teams who don't care that they might change their mind in 10 years.
I can’t think of a better way to develop an appreciation of UI frameworks that to go without.
> I want the decisions I make to last decades, not just a few years. I don't think that's a sentiment appreciated by most, though.
Barely any software runs untouched for decades (documents don’t count). So it’s not that the sentiment isn’t appreciated, I think most of us would agree—it’s that it’s an impractical expectation.
When you maintain your own software for periods of time that outlast front-end engineering trends or IC's entire career tracks, your opinions might change.
Plenty of software runs untouched for decades. A lot of it powers your interactions with people on HN right now, from drivers, to font rasterization and backing layer UI composition. There are bugs in codebases that have taken 20 years for people to even notice. It happens.
When people don't have to worry about trivial details, they can focus greater efforts.
At once I sympathize with your viewpoint and also have to say that React is the longest I've ever seen a Javascript thing stay in the notoriously fickle good graces of the frontend world – short of perhaps jQuery.
Railing against it is railing against the best-in-class example of what you want... even if it still falls short of what you want. A decade is nothing to sneeze at.
> obscuring magical details and making side effects seem like more of a big deal than they really are in programming seems like a design choice intended to infantilize engineers and shelter them from reality.
You seem to be taking it too personal. There's no need to call others infants for preferring hooks.
> `Document.createElement()` and use native event handling.
I'm not a frontend dev by all means, but I must say, I've worked on such projects (100LOC create utils & event handling) and it was a joy. I always wanted to recommend it to people when they complain about the state of js frameworks, but I felt I've no authority to do so, because my experience is limited (only internal projects).
I don't think it's productive to throw out enotionally-charged words like "fanatical" and "infantalize" in these conversations.
There are many valid complaints to make about React hooks, but I'm not really seeing those here. And I'm not seeing evidence that you've crossed chesterton's fence with them either.
I'll criticize hooks all day, but for all their footguns, they provide a level of abstraction (and a simplicity of implementation for it) that's really hard to argue with. They let you break up reactive stateful code in maybe the most scalable way I've ever seen, and their contract with the outside world allows for some crazy optimizations on the framework's part. I think the team is onto something really special here
Of course they're also easy to misuse, and they can be really "magical" until you fully grasp them. Those are problems the ecosystem will have to grapple with (and I know the core team is aware of them). Though the "magic" at least is due more to weirdness and inversion of control than it is to actual complexity. Having a grasp on how they work, I feel like I wouldn't have too much trouble implementing a basic version of the same system myself, because the primitives are ultimately not very complicated
I believe hooks are really good bones for building UIs, and I think they'll last because of it, even though the surface developer experience has some warts for now
I think it's sufficient to say that having been exposed to hundreds of thousands of lines of code across different industries and programming languages, I don't like React Hooks, and I don't even like them repurposing the word "hook" which is otherwise known as a trampoline in other programming language contexts.
React Hooks, if anything should have just been called React Callback Queues.
As for emotionally charged words, maybe it's not appropriate in the current zeitgeist to post without some degree first of self-censorship, but it's how I feel, so I'm going to say it.
React Hooks were fanatically adopted, in my opinion. Side effects are a regular part of programming. I find it, thusly, infantilizing to hide those details.
I wish people would stop this sort of thing, but if you want to say your part and that's how you feel, say it too.
Edit: You've apparently even been downvoted for expressing this sentiment, which I hate on HN, because it's how you felt. I wish social sites wouldn't do this. No one should be able to invalidate that.
> front ends independent of any of the existing frameworks that exist today, which I think is probably controversial
Not sure controversial is the right word, in fact I think it is quite a common sentiment among developers, especially those that originally come from back-end. The only problem is finding a company that is willing to forgo the frameworks.
They do exist though. Here's a short list of companies that use vanilla JavaScript, most notably GitHub and Netflix:
> I want the decisions I make to last decades, not just a few years. I don't think that's a sentiment appreciated by most, though.
Clojure and ClojureScript very much appreciate that sentiment. re-frame, a library for creating React apps with ClojureScript is rock solid, many years old, and still on version 1, meaning no breaking changes so far. 5 years old re-frame code still looks the same today.
The docs are kinda poor on them IMHO. They don't approach the hooks from first principles. For example look up the useImperativeHandle hook docs. Use those to explain simple questions:
> Why do you need this?
> Why can't I just update the ref however I want!
Look up forwardRef:
> Why can't I just access ref as a prop?
Further, everyone is trying to cram all logic into hooks and components. It's to the point of insanity. Look, React is so popular I know most are using it for throw away marketing sites and other low-tier shit (sorry y'all but you know it) but.. This architecture doesn't fly in large apps; ee b2b etc.
React Router v6 seems to have no non-hook based APIs that aren't marked private!? They screwed the pooch on useNavigation; it causes unnecessary re-renders. Surprise surprise, but if they can't get it right...
I‘d love to have a simple way of calling ReactDOM.renderToString() on my express server that does block to actually wait for API calls so that SSR‘ed HTML is populated. I am very willing to trade performance against simplicity here.
Besides some explorations for suspense and server components, there doesn’t seem to be any straightforward solution to that problem unfortunately
I'm always wondering, why it took web devs so many years to create some meaningful reusable UI components (and I still don't see a wide adoption of something like that)? I just can't understand how reimplementing even such things as simple buttons every time from scratch is productive
Currently microsoft product line like SharePoint, Dynamics (Power Apps), Office, Teams, etc, etc is based on FluentUI https://developer.microsoft.com/en-us/fluentui#/ components that gives also the developers a close enough solution to extend UI within said products.
Because there is a lot of creativity involved when you are placing pixels on a screen. You’d be surprised by how many different ways you can render a button. There is no single abstraction that satisfies all business requirements.
213 comments
[ 3.4 ms ] story [ 151 ms ] threadI'm glad they fixed the janky scrolling though that has been cracking me up for a while. It's an example of a common complaint/pitfall with react and even the official docs were plagued by it. As a heavy react user and light react hater I love to see that shit lol.
Is basically useless when scrolling. All I see is a gray background, and when I stop the content disappears. Makes it hard to quickly skim to the correct section.
If you "inspect" the elements with the mouseovers, you can see they statically compiled the TS definition into the HTML.
I guess it probably uses the same APIs to read the types VSCode does, given what they state the purpose of Shiki is.
A tremendous improvement though, and it must have been a lot of work coordinating, especially the easier to digest images sprinkled throughout.
At last we can officially move past the era of class components.
Hilarious! As someone who was once a "full-stack" web dev in the age of jQuery, CSS resets and local fonts only, this definitely rings true.
And having just started learning React (through Next.js), this is a timely and welcome refresh! Thank you!
Having a dedicated place to process changes in props and setup/teardown logic is just generally nicer than weird dependency arrays and returning destructors from effects.
Class components are just (IMO) cleaner, and I find myself saddened at the level of disarray most codebases with hooks are nowadays. Enough has been written elsewhere about how hooks require one to keep more in their head; class components have an agreed up layout/structure/etc that is important in large codebases.
It's just frustrating. Couldn't they have had the decency to fork / rename React for all this and leave the old branch to die instead of turning the entire space into some kind of pedantic war-zone for several years?
We recommend defining components as functions instead of classes.
Not dead but they are not recommended anymore.
See here for some examples of how you can approach writing the same code in a few different ways -- maybe one of these styles will fit you better: https://react.dev/learn/reusing-logic-with-custom-hooks#ther...
is there a good reason not to uniformly rely on useReducer everywhere in a codebase?
There are downsides to this - you do need to think a lot more about code architecture before implementing the code, and you can get into a really messy/unmaintainable situation if you aren't careful. And you'll need to consider what state your "singleton" is tied to - is it really one per page, one per component, etc? And depending on the answer, the implications may be that this pattern isn't a great idea. But for certain use cases, it can work well.
The core premise of this pattern is separating business and UI logic, which is not exactly a new idea.
[0] https://www.searchkit.co/
[1] https://github.com/algolia/instantsearch
[2] https://github.com/searchkit/searchkit/blob/main/packages/se...
React apps should, in general, contain much less react-specific code than they tend to.
In the popular sense, they've been on the down and out for years now.
> It turned in to a bit of a mess until I went for a refactor into classes and everything became much more clear.
in my experience, once the logic starts to become complex, you need to develop custom hooks so that a given component stays readable.
The hooks paradigm is a lot harder once you get past the basics, but I wouldn't go back for reasons I could articulate if there's any interest.
I hate hooks. The syntax feels nice to type, but the issues outweigh the benefits for me. It’s way too difficult to understand the rendering lifecycle, the state updating, the ordering and I also find the reuse abstraction hard to follow (although I accept that might be a concentration/attention issue on my part). Conversely, it’s also way too easy to break the purity of the hook callbacks, so hooks can use state from contexts you wouldn’t expect (more an issue with custom hooks if you don’t also supply linter tooling to go with them).
Why do you prefer them?
Obviously using hooks doesn't mean it has to be shitty, I think it just brought a lot more wannabe React devs that have no experience with architecture or maintainability.
I tried to give it a real shot with hooks, but the marginal benefit they bring seems to be far outweighed by the added complexity. Add to that the mixing of paradigms with some functional components, some class-based, etc and it becomes a mess pretty quickly.
The hook system is the parts of an OO system that React needed to be able to keep developing features & optimizations for function-based components, rather than telling their users & devs that they'd simply have to use classes for some things. It exists so they could side-line class components, rather than having to become outright reliant on them for some features. So, pretty much, yes.
Class components are still the only supported way to create an error boundary. Other than that, they are pretty much dead, yes.
This worries me a bit because some React wrappers for ClojureScript expose macros that essentially compile to React.createElement() calls, which are now labelled as a legacy API.
It also looks like Class Components are officially deprecated, given that the documentation explicitly states they are not recommended for use in new code.
We're not removing createElement but I'd recommend to change your wrappers to the same compile output that the new JSX transform (introduced in 2020) uses: https://legacy.reactjs.org/blog/2020/09/22/introducing-the-n.... The new JSX compile target will allow us to do a bunch of optimizations in the future that createElement() can't.
I'll make a note of adding this to the docs.
In our embedded/plugin component scenario where we are given a <div> to load in, it appears we should replace our current pattern ReactDOM.render(React.createElement(... with createRoot(_jsx(....
Yeah you want
const root = createRoot(domNode) root.render(<Stuff />)
(or the JSX transform output equivalent)
Edit: there are a lot of good--and varied--explanations here. Which is why I think the docs should cover it in-depth. It's confusing.
If you were to call them in an "if" branch it would mess up that queue mechanism.
2. React counts the number of executions of (certain) hooks. This count is how it knows which state to get from the store as a return value from `useState`. useState is effectively `getStateAndSetter()` but it doesn't pass a key name of any kind, so the implicitly passed key is `hookCount++`. This is why you can't call hooks conditionally, or state would get all messed up - if a condition turns false and one hook doesn't run that render, all getStateAndSetter calls that run after it will be off by one.
Additionally, this context would also have the context of children elements, so things can actually be persisted across function calls and React can know what needs to be mounted & unmounted.
Also note that because Tagname isn't called directly, it's also how React is able to do its diff and only actually call what is needed.
This is also why if you're generating a dynamic number of elements (ie outputting an array of elements), you should provide a `key` prop, so it can link up the elements to their corresponding past states each time the function runs.
In reality we use a linked list rather than an array. If you wanna dive into the code, I can give some pointers. For example, useState is implemented like this during first render (https://github.com/facebook/react/blob/87c803d1dad7e5fe88634...) and like this during next renders (https://github.com/facebook/react/blob/87c803d1dad7e5fe88634...).
However, _conceptually_ I'd recommend to think of Hook return values similar to "extra inputs" to your function, kind of like extra arguments. There are different ways to formalize it in different languages. We picked plain function calls for simplicity and low overhead, although you could imagine `yield` with generators or something like that.
- React has a module-scoped variable in the hooks implementation file that tracks which component is currently rendering
- The internal "Fiber" data structure that describes a component instance has a linked list of hook contents (saved values and callbacks)
- As each hook gets called, React tracks the current hook entry, looks up the current hook data, and returns it
So, the number of hooks used needs to be the same each time the component renders so that the linked list entries match up consistently.
There's a great talk by @swyx here that builds a miniature hooks implementation in about 30 minutes:
https://www.swyx.io/hooks
So there is a data structure that store says `[useMemo, useState, useEffeect]` - and when you component re-renders, is unmounting, or has effects to trigger it uses the index order to lookup the bit of state that needed to persist.
This is a pretty good high level explainer of how react works that touches on some of that: https://overreacted.io/react-as-a-ui-runtime/
1) A hook adds a function or variable to one of several lists attached to the component object, when it's instantiated (yes, even your "functional" components are objects, and I don't just mean in the JS-functions-are-actually-objects sense—at least, they were when I read it)
2) Subsequent calls either call those functions, or accesses/modifies the value, in a FIFO manner, reading them out of those lists. This is why you can't mess with hook ordering in e.g. loops.
It's basically just methods and properties on an object, but with FIFO access based on declaration order, instead of using a lookup table of some sort.
[EDIT] A poster correctly pointed out (then deleted their post) that I wrote "loops" where I meant "conditionals". Technically sorta-true (though not quite, as phrased) if the loop isn't the same length every time, but yeah, I meant conditionals. Point is, the reason order matters is that the whole thing's just a bunch of FIFO queues, more or less.
Within the component functions's body, you may have many calls to the same hook, lets take useState() as an example.
Each useState() returns a different state variable, that is kept track of in a cache outside the component function.
On the first render, the state variables are created. On subsequent re-renders, the state variables are read from the cache.
The cache is a simple array. It keeps track of, and identifies each individual state variable from it's index in the cache array.
The first call to useState() gets slot 0 in the array, the second call gets slot 1 and so on and so forth..
For the tracking to work consistently, all calls to useState() within the function's body must also happen consistently.
In the same order, every time. Having a useState call within a conditional "if" branch breaks that consistency.
I think a good example of where React sits is Deno. The devs who are working on Deno don't seem to have much interest in React, but they are pushing a React framework, Fresh, to make it popular with regular devs. They see the popularity of React and not those who are frustrated with it. One thing is how much typical React code relies on a build step and a bunch of Provider objects. CSS Variables can help make components customizable without having to do CSS in JS.
I think Web Components, maybe with Lit or Svelte, are making more sense for beginning devs. With those you don't have to worry that you might need to work on non-react code sometime.
Beginner devs want to get a job, so they should probably spend their time learning the framework that dominates the ecosystem. Lit and Svelte are cool, but I don't think they're a great target for a first time web developer. Svelte maybe. But definitely not Lit - it's a relatively new library and a moving target without a lot of adoption, meaning there is a sparse ecosystem to fall back on, and you'll need to fill in a lot of gaps yourself (both in terms of libraries for common functionality, and docs/stackoverflow answers for telling you how to do things). Experienced devs can read the source and official docs to figure it out, but newbies need more hand holding.
Yeah, that's the point I'm making about Deno.
If Deno's own devs avoid anything similar to React hooks except when they're trying to appeal to the beginning devs, perhaps it would be smart for beginning devs to try to do as the senior devs do, not as they say?
The ecosystem of Lit is the web, which has a lot of great stuff like MDN. It lets you simply use what you learn there. No redirection, like React's onChange translating into the input event.
I agree with this, and I mostly empathize with the purity aesthetic that comes with it. But I think in practice, you need to do a lot of work for common operations that might have entire libraries dedicated to them in React. If you're a pro developer obsessed with purism, you probably wouldn't use those libraries anyway. But newbies don't have time to worry about re-inventing the wheel (or at least, we shouldn't encourage them to do that, since it will probably be a pretty shitty wheel).
All that said, I absolutely love Deno, and I think we should encourage new developers to use it, especially since it sidesteps the need for build steps in many cases.
For what it's worth, the site has been developed by different people over time, so the choice to use Tailwind was made by someone else early on. The team working on the site now doesn't feel strongly about it either way — it's sometimes annoying but overall using it feels really nice! And I'd probably say the same about other CSS solutions too.
I think we'll keep it for now. Where it really shines IMO is fast prototyping. But yeah, it's cool.
Edit: Ok ok tbh I do like it.
React clicked really fast, documentation new and old, articles helped get on track really fast.
Wiring that all together and taming CRA (Create-react-app) with react-app-rewired to add stuff to webpack, like adding libraries to behave like modules that are NOT modules, packing all with scripts into single html, understanding where are boundaries between fluentui and react (both new to me), setting up monorepo because I separate reusable components from app itself and libs, applying css which I am bad at and stuff like that took more time... complexity just explodes, but less "mental effort" overall achieved by having streamlined build, reusable stuff etc.
Otherwise I feel that building app with react takes a lot of "mental effort" away, because you develop a component in isolation which feels simple and when you use that component you don't think about implementation details - it is nicely abstracted away and things just work.
Ah, yes, and I'm lucky I got the signal that functional components are way to go and the only place I had to use a small class component when I want to have a component variant for existing class based component from FluentUi where generic types are important and used within `onRenderItem` and various methods:
> class LookupBaseInternal extends BasePicker<ILookupResultProps, ILookupPropsInternal> {}
> There is currently no way to write an error boundary as a function component. However, you don’t have to write the error boundary class yourself. For example, you can use react-error-boundary instead.
https://react.dev/reference/react/Component#catching-renderi...
Anyhow… To this day, I still think it’s an unforgiving, crazy beast of a library for a weekend hobbyist developer like me. But I kinda like it now and in big part it’s thanks to hooks, functional components and these new docs. Good job team, and congratulations on finally getting this live!
Finally, if you combine it with the fact that it seems to hold on as the industry standard for jobs, and being useful for native mobile development via React Native, it all comes together nicely in my mind. With its huge ecosystem and closeness to plain vanilla JS, I guess it just makes me simply a bit of a better developer and gives me a new perspective on things. That's that.
Regarding performance, I remember having blocking sluggishness with v-model that shouldn’t have been there back in Vue 2 days.
Also, for example, in Vue 3, my favorite component library Buefy (based on Bulma) is no longer available, and I don't think any of the other ones have (arguably) come so close and extensive to what I want.
Vue 2's transition to Vue 3 (if you want to use the new CLI tools and the Composition API) had to, by definition, fragment the ecosystem in unpleasant ways no matter how carefully and sensibly it was managed by the core teams in charge.
You can see that Vue went down slightly in recent developer surveys, and I suspect this to be among the main reasons. As if Evan himself would be suddenly more focused on his build tool Vite as opposed to Vue itself (of course it's just an illusion).
But I still love Vue because of their amazing docs, and the fact that they let me enter the world of web-development in a relatively accessible manner (this may not have been possible with React, for me personally). Lack of something like scoped styles alone baffles me in React to this day, but it makes kinda sense in the ecosystem it's operating in...
So many useMemo, useEffect, useCallback madness was trivial with basic class component lifecycle.
Supporting some features for hooks and classes, both, probably would have meant having two implementations in some cases, that might not quite match up as equivalent. More API surface area, more code to test, more combinations and paths to worry about.
> And aren't classes just really functions under the hood anyways?
Other way around, actually: "functional" components end up represented by an object.
In other paradigms, it is! Our paradigm is exploring the functional take. I agree it's a bit unorthodox but we are very intentional about modeling it that way. It really has a bunch of powerful properties one might not expect.
>And aren't classes just really functions under the hood anyways?
The key difference is that in React, UI is a pure projection of current data (props/state). You're always supposed to "return" the UI. Sure a class is a function, but that function is invoked once. Its methods can be called many times, but having a pure render() method (like in class-based React) is really a class cosplaying as a function. Functions are more honest to what React is trying to be.
Relevant part from Seb's Hooks RFC comment (https://github.com/reactjs/rfcs/pull/68#issuecomment-4393148...):
> Classes may seem like the ideal thing to hold state since that's what they're designed for. However, React is more written like a declarative function that keeps getting executed over and over to simulate it being reactive. Those two things have an impedence mismatch and that keeps leaking when we think of these as classes.
>Another issue is that classes in JS merge both methods and values on the same namespace. This makes it very hard to make optimizations because sometimes methods behave like static methods and sometimes behave like values that contain functions. The Hooks pattern encourages the use of more statically resolvable calls for helper functions.
>In classes, each method has its own scope. It causes issues like us having to reinvent default props so that we can create a single shared resolved object across those. You also encourage sharing data between those methods using mutable fields on the class since the only shared thing is this. This is also problematic for concurrency.
>Another issue is just that the conceptual mental model for React is just functions calling other functions recursively. There is a lot of value to express it in those terms to help build the correct mental model.
For a concrete example of where classes as a model fails us, consider useTransition (https://react.dev/reference/react/useTransition). It lets you start rendering "in background" with a different state value. But if you get interrupted, the renders have the current value. This highlights that in React, the same piece of state can conceptually be thought of having more than a single value (kind of like being in parallel worlds). Classes don't model that well.
The operative word there is 'exploring'. In practice, hooks are still basically OOP, albeit masquerading as FP. You just shuffled the state into a shadowy realm adjacent to the component where it's harder for developers to see. I know a lot of JS developers are allergic to writing the 'class' keyword (even though JS doesn't really have classes in the first place), and I guess hooks help them sleep easier at night by allowing them to believe that they're writing their code in a functional way. But they usually aren't. And I think that misdirection is the source of a lot of the confusion out there regarding hooks.
"The greatest trick the OOP devil ever pulled was convincing the world that state didn't exist"
Even if we set aside implementation inheritance, class hierarchies, and all that jazz that got associated with modern OOP, fundamentally classical OOP is message passing. React components don't pass messages to each other in that sense. The data flows strictly down. Re-rendering is not message passing but conceptually reevaluating a part of a lazy continuously reactive function call tree.
It's not about "sleeping easier at night" etc, it's just a different model. If you're curious to entertain this idea for a bit, I have an article you might enjoy reading: https://overreacted.io/react-as-a-ui-runtime/
> fundamentally classical OOP is message passing
OOP in its essence is about using conceptual data models as state containers. Cats, trees, input fields. Message passing is just the means by which these containers interoperate. The persistent state is the defining element.
Judged in this light, I'm arguing that the way most people write React components is more OOP than functional. The components maintain some state inherent to their nature. And I would argue that these components are still passed messages, or at least one: "render". When a component is passed the "render" message, it uses its internal state to output the appropriate DOM structure. A news widget maintains the list of headlines to display. An expanding menu holds the state for which menu items have been expanded. A form holds the errors that have been triggered from input field validation. And so on.
Thus, each component is still essentially a polymorphic implementation of an interface with a "render" method: an "instance" of a "class" that encapsulates state, even if the actual language-level class doesn't exist anymore. The mechanics are essentially the same. Since "render" was the only method being invoked on the component class, you took that lone method and promoted it to a standalone function that is invoked directly. And then you took the internal state of that class, and moved it behind the hooks API. That way, the "render" method can still access the class's internal state despite the absence of the "this" keyword. That's what I meant by misdirection. The class has just become virtualized.
Obviously it's possible for people to write React code in a way that avoids local component state. Naively we could just do prop drilling, in which case your argument that "the data flows strictly down" would actually be true (as opposed to being commingled with the data that is being stored in local component state at various levels). Or we could use Redux components, which, when written "correctly", are essentially pure functions that accept the entire application state as an argument. Et cetera.
Maybe that's actually your philosophical vision for how people should be writing React code, in a frictionless-plane-of-Platonic-ideals sort of way. Personally, mine is pretty close to the Redux model: components are just a Pachinko machine of nested functions that emit UI, and my application state lives outside of that machine, and is fed to it as an input.
But that's not how most people actually structure their React code. Instead, they continue to model their application as an aggregation of conceptual objects a la OOP. Forms. Widgets. Color pickers and the like. Each with its own persisted state. The fact that you've architected React to avoid semantic classes is orthogonal to the fact that people are still using it to implement conceptual classes.
Can you elaborate on this?
> a class is a function, but that function is invoked once. Its methods can be called many times, but having a pure render() method (like in class-based React) is really a class cosplaying as a function. Functions are more honest to what React is trying to be
useState() is a function cosplaying as a class ;) and that's really my biggest hangup. It seems like react components are not quite functions nor are they classes but rather somewhere in between... And because of JS quirks and perhaps the internal architecture/mental model, functions end up being a better choice.
> in React, the same piece of state can conceptually be thought of having more than a single value (kind of like being in parallel worlds). Classes don't model that well
Maybe I'm missing something but this seems like a false dichotomy because the equivalent in the class-based model would be the render function, not the class itself. And in that sense, your set of render functions can also have multiple values given a state. Though just writing this out does make functions feel a bit simpler.
Admittedly, you've mostly convinced me! And after reading through the new documentation, going function-only feels a lot cleaner than it did when I learned react using the old docs.
And they just handwave it away in the docs with an imaginary future API. Embarrassing.
https://github.com/reactjs/rfcs/blob/useevent/text/0000-usee...
- To avoid re-triggering stuff from Effects, we _are_ adding a Hook. It has the same API as the original `useEvent` proposal but is more tightly scoped to this particular use case (and different semantics). We'll submit a new RFC for it after some more testing, but it's available (as `useEffectEvent`) in experimental builds, and the docs mention it: https://react.dev/learn/separating-events-from-effects#decla...
- To improve rendering performance, we are working on an automatic compiler that analyzes your code and makes rendering much more granular. It's still in active R&D but we hope to share an update on it soon.
We think these two things together will likely be able to mostly address the issue. If not, we'll look at the specific gaps and work on them more closely.
In general though, people have shipped huge apps with Hooks, and it's definitely production-ready. Always an opportunity for improvement, sure.
And what do we get from the React team? After five years? More vague promises. More RFCs. More blog posts. More tweets. More docs mentioning nonexistent APIs. No actual shipped solutions. Forgive me, but I just don’t believe you anymore at this point. Five years.
You’re obviously a bit frustrated but as someone who has done a lot of React over the years I don’t quite get what you’re running into that would make you that upset.
I think it's fair criticism we've been slow on shipping this Hook. We try to take a very cautious approach to introducing new APIs, so we are currently testing it in our internal codebase. Once we feel confident the approach makes sense, we will make a stable release with it.
There’s always a lot of complaining about hooks but honestly I think they’re generally pretty wonderful. Occasionally you need to do some gymnastics but more recently I’ve lent on the useEvent pattern to detach reactive and non-reactive code and it’s working nicely.
Is there something I should know about useEffectEvent vs useEvent? We use the pretend / poly fill version and I understand it behaves slightly differently. But in general we just want a function that doesn’t change that always calls the latest version of the real function. Mostly we’re passing it down to other components that use it as an event handler or sometimes in a useEffect themselves.
As ever, thanks for the work on React.
Yea. It still proxies to the latest version, but it doesn't give you a stable function. (In fact, it always gives you a new one in DEV to avoid depending on its identity.) Instead, the _linter_ lets you (actually, forces you) to omit it from dependencies. Conceptually, it's a non-reactive piece of code. There's also a new limitation that you're supposed to only call it but not pass around (also enforced by the linter). There's a bunch of reasons for this design which we'll write up in the RFC. (TLDR: you only really know whether something should be reactive or not next to the actual callsite.)
Hooks has made me 3-4x more productive. If a technicality is stopping you from enjoying it, that is such a shame.
The world has embraced hooks, for better or worse. The fact is React still works well, and the React team has always been clear that performance is (somewhat) an implementation details. For example, they often advertise to use inlined functions and to use `useCallback` only if you're facing performance issues.
So `useCallback` invalidation is definitely not "fundamental" (imho)
Honestly, by the time class components are fully deprecated, you'll probably be able to ask ChatGPT to rewrite your codebase to use hooks instead...
I hope the irony of criticising a team for launching something early, on HN _of all places_ isn’t lost on you.
> going all in on hooks
They went all in on hooks in 2018 when they rewrote the internals with React Fiber.
Hooks are how react works, class components are more of an abstraction—not less.
> while the fundamental problem with them, which was raised in 2018 is still unsolved
If it was a showstopper it would have been fixed by now.
https://github.com/mdgriffith/elm-ui
https://styled-components.com/
https://vanilla-extract.style/
From the style of writing, to the style of site, I can tell a lot of effort was put into this. Will check out react this weekend - it's been a couple of years (we've been using svelte)
Side note: I recently took an assessment test on React through Triplebyte and was given an expert level badge (for what it's worth). I think that speaks volumes about the new React docs because that is where I gained most of my React related knowledge.
Here's my implementation, for the record: https://pastebin.com/rjj0FDDd
I'm disappointed by the fanatical adoption of hooks, but I saw it coming and I can't say their legacy documentation didn't warn me.
I'm happy that other people seem to enjoy them without restraint, but obscuring magical details and making side effects seem like more of a big deal than they really are in programming seems like a design choice intended to infantilize engineers and shelter them from reality.
I might finally invest some time into what it looks like to create front ends independent of any of the existing frameworks that exist today, which I think is probably controversial, but I want the decisions I make to last longer than the whimsy of engineering teams who don't care that they might change their mind in 10 years.
I think having seen front-end software come and go so many times, I'd rather write some simple utility functions wrapping `Document.createElement()` and use native event handling.
Too much fluff in front-end.
I want the decisions I make to last decades, not just a few years. I don't think that's a sentiment appreciated by most, though.
Oh right, you meant React wise... yeah...
I didn’t want to adopt functional, but let everyone make their own decisions on what to use, and now I’m very happy I did.
Asking for an API interface to be stable for decades __across all future versions of the library__ doesn't sound realistic to me. Nothing is forcing you to update to the future new version of react - which has not been released, and is likely years away - that removes support for class components.
For a lot of Linux distributions, you have a problem and you go online to find a solution. Depending on how old the stackoverflow (or w/e) answer is - the answers you find are out of date. So you find yourself sifting through a pile of possible answers and you need to figure out which answer applies in this context.
Compare that to FreeBSD where it feels like every effort has been made to stay backwards compatible with the legacy APIs for decades. I found a web-book on FreeBSD network administration once and almost dismissed it because it was last updated in the 90s but, upon closer inspection, most of the book was still relevant and I could administer a FreeBSD network using the same commands the book was using!
As an industry I do think we devalue tribal knowledge and the accumulated ecosystem of documentation that gets invalidated with every breaking change. Every time you ship a breaking change all of those hard learned lessons across all of your users that are sitting in the back of their gray matter, in their blogs, and in stack overflow answers all become invalid - at best they get removed/updated and at worst they stick around and mislead future adventures.
It seems they are getting bored.
It's a joy to work in, feels "frameworky" but it's just web standards with <100 lines of convenience JS wrapped around it. There is no magic beyond what the browser provides - I like it that way.
https://github.com/retrohacker/template/
It's "open source" as a reference. Just using it for myself. There aren't many docs beyond notes to myself. But the actual framework is a 90LoC JavaScript file that is an easy read.
You're welcome to kick the tires. If you like it I'd entertain PRs and stuff but it's such a small library forking is probably entirely reasonable to make your own flavor too.
The general idea is you extend the Template base class and call "super" with the id of the <template> that will get bound to the ShadowDOM when the class is mounted. Then you call instance.mount and pass a dom node to mount it into the DOM. For child nodes, you use `this.fragment.querySelector` to select them from the <template> you mounted. It supports garbage collection by tracking children, so when you "unmount" it recursively unmounts all child instances as well. Finally it has an event emitter implementation, so changes/actions/events bubble up the DOM while state can push down through the DOM. Keeps things clean.
I recently added state methods since I was duplicating state management everywhere. Now the base template class has a `setState` that will emit a single `change` event for all changes in the current "tick" of the browser eval loop.
Cheers and happy hacking!
Edit: Hey another Arizona hacker! Right on!
Just checked out your website. I'm also doing Software consulting in Arizona, would love to grab coffee (digital or otherwise) and compare notes.
If you are in the PHX area, checkout https://www.heatsynclabs.org/ - they do Coffee & Code every Wednesday.
If you make changes, I'd appreciate a follow up GitHub comment that lets me know where you made improvements, but no obligation. It's licensed under a BSD-style license so you're free to do with it whatever you like!
The entire front-end framework landscape is like this. It's all designed to appeal to the kind of "engineer" that just wants to copy paste code and have it work like magic without ever thinking about what's actually going on.
It sucks that people learn this the hard way, though, and the only way to grow beyond this for people new to the field is to eventually get burned and learn there are other ways to do all of this.
There are whole generations who don't know what session cookies are! And... that's just OK.
Let’s better spend time on important things instead of wasting time trying to make UI behave properly across a plethora of browsers, OSes and platforms.
Cross-browser compatibility was the problem that jquery was trying to solve back in 2008 or thereabouts. Not a problem that react is trying to solve in 2023.
My biggest gripe with them as a technology is that they have no way to associate a `<style>` tag in `<head>` with a ShadowDOM tree (short of cloning and injecting w/ JS).
The only way to say "this CSS goes with this <template>" is to nest the `<style>` tag in `<template>` itself or painstakingly expose `part`s on everything you want to style.
I'd really like a template selector for CSS where I can say something like `::template(#id) button` to select all buttons inside the `<template id="id">` for whatever ShadowDOM it is mounted to.
Without this, I either have to do CSS in JS, CSS in HTML, or I have to use special build magic to take my standard index.css files and inject them into my HTML or JS. All of that is kinda gross.
I think it's more charitable to assume that the designers are designing this for themselves and are just being aware of their own fallibility and limited mental capacity (we're only human after all), not seeking to infantilize or shelter some lesser class of programmers.
I'm running two businesses with a third on the way. I have bigger problems than people reinventing stuff with no benefits compared to what we were doing 20 years ago.
I'm writing software every day, so I'm still involved in front-line work, but I'm interested in being the level of productive most people aren't.
So the bigger questions on my mind are things like, "What knowledge can I build up that doesn't become obsolete?" "What social effects drive adoption that endanger those goals?"
Those are questions that have, at least immediately, very little to do with implementation details, and are questions that help me navigate whether to ignore new technologies all together, or when I identify something new when I decide to adopt it.
A part of that is looking for cues from maintainers that say, "Hey, we care that you're shipping, and we're not going to endanger your labor spent learning our intellectual property."
This is a great idea. Nothing controversial about it.
Lots of engineers out there today who don't have a simple answer for, "So what happens when one of your dependencies no longer exists on the Internet for one reason or another?"
It's a low likelihood, but I'd rather stay up to date when I can.
The writing has been on the wall for 4 years that hooks are the future. You _can still_ use class components. Function component with hooks is the simplest API you can get to React itself—classes are more of an abstraction.
> I'm disappointed by the fanatical adoption of hooks, but I saw it coming and I can't say their legacy documentation didn't warn me.
We all adopted hooks because it makes things easier to reason about. If you’re still having trouble understanding them, I’d urge you to dig deeper into how they and React works.
> I might finally invest some time into what it looks like to create front ends independent of any of the existing frameworks that exist today, which I think is probably controversial, but I want the decisions I make to last longer than the whimsy of engineering teams who don't care that they might change their mind in 10 years.
I can’t think of a better way to develop an appreciation of UI frameworks that to go without.
> I want the decisions I make to last decades, not just a few years. I don't think that's a sentiment appreciated by most, though.
Barely any software runs untouched for decades (documents don’t count). So it’s not that the sentiment isn’t appreciated, I think most of us would agree—it’s that it’s an impractical expectation.
Plenty of software runs untouched for decades. A lot of it powers your interactions with people on HN right now, from drivers, to font rasterization and backing layer UI composition. There are bugs in codebases that have taken 20 years for people to even notice. It happens.
When people don't have to worry about trivial details, they can focus greater efforts.
Railing against it is railing against the best-in-class example of what you want... even if it still falls short of what you want. A decade is nothing to sneeze at.
You seem to be taking it too personal. There's no need to call others infants for preferring hooks.
I'm not a frontend dev by all means, but I must say, I've worked on such projects (100LOC create utils & event handling) and it was a joy. I always wanted to recommend it to people when they complain about the state of js frameworks, but I felt I've no authority to do so, because my experience is limited (only internal projects).
There are many valid complaints to make about React hooks, but I'm not really seeing those here. And I'm not seeing evidence that you've crossed chesterton's fence with them either.
I'll criticize hooks all day, but for all their footguns, they provide a level of abstraction (and a simplicity of implementation for it) that's really hard to argue with. They let you break up reactive stateful code in maybe the most scalable way I've ever seen, and their contract with the outside world allows for some crazy optimizations on the framework's part. I think the team is onto something really special here
Of course they're also easy to misuse, and they can be really "magical" until you fully grasp them. Those are problems the ecosystem will have to grapple with (and I know the core team is aware of them). Though the "magic" at least is due more to weirdness and inversion of control than it is to actual complexity. Having a grasp on how they work, I feel like I wouldn't have too much trouble implementing a basic version of the same system myself, because the primitives are ultimately not very complicated
I believe hooks are really good bones for building UIs, and I think they'll last because of it, even though the surface developer experience has some warts for now
React Hooks, if anything should have just been called React Callback Queues.
As for emotionally charged words, maybe it's not appropriate in the current zeitgeist to post without some degree first of self-censorship, but it's how I feel, so I'm going to say it.
React Hooks were fanatically adopted, in my opinion. Side effects are a regular part of programming. I find it, thusly, infantilizing to hide those details.
I wish people would stop this sort of thing, but if you want to say your part and that's how you feel, say it too.
Edit: You've apparently even been downvoted for expressing this sentiment, which I hate on HN, because it's how you felt. I wish social sites wouldn't do this. No one should be able to invalidate that.
So they're still not breaking backward compatibility. Looks like you don't have to refactor until you need something that's only hooks based.
Not sure controversial is the right word, in fact I think it is quite a common sentiment among developers, especially those that originally come from back-end. The only problem is finding a company that is willing to forgo the frameworks.
They do exist though. Here's a short list of companies that use vanilla JavaScript, most notably GitHub and Netflix:
https://gomakethings.com/companies-that-use-vanilla-js/
Clojure and ClojureScript very much appreciate that sentiment. re-frame, a library for creating React apps with ClojureScript is rock solid, many years old, and still on version 1, meaning no breaking changes so far. 5 years old re-frame code still looks the same today.
> Why do you need this?
> Why can't I just update the ref however I want!
Look up forwardRef:
> Why can't I just access ref as a prop?
Further, everyone is trying to cram all logic into hooks and components. It's to the point of insanity. Look, React is so popular I know most are using it for throw away marketing sites and other low-tier shit (sorry y'all but you know it) but.. This architecture doesn't fly in large apps; ee b2b etc.
React Router v6 seems to have no non-hook based APIs that aren't marked private!? They screwed the pooch on useNavigation; it causes unnecessary re-renders. Surprise surprise, but if they can't get it right...
Besides some explorations for suspense and server components, there doesn’t seem to be any straightforward solution to that problem unfortunately