Hacker News new | ask | show | jobs
by throwitaway1123 905 days ago
> next server components breaking most css-in-js libraries

This is a legitimate gripe, and React Server Components in general have introduced a lot of complexity. It's worthing noting though that you don't have to use server components and Next's new App Router. The Pages Router still works. I'm ok with a framework occasionally (not excessively) introducing a new way of doing things as long as the old way is still supported (much like React still supports class components despite the introduction of hooks).

1 comments

My point is that this sort of thing doesn't happen so lightly in other ecosystems. And the "you don't have to use it" excuse isn't used when the project clearly wants to move in that direction:

- others will jump on the feature and use it. you might not have to use it but libraries you depend on may decide otherwise and rewrite

- you didn't have to use ReactHooks, but try using Apollo client or react-query without them now, or try finding any non-hooks documentation in general.

- you will slowly see bugs being neglected if they're not about the new and shiny.

- there is no equivalent of useContext for class components

So the word "supports" here carries very little weight in practice because the rest of the ecosystem constantly rewrites.

> My point is that this sort of thing doesn't happen so lightly in other ecosystems.

React has been around for 10 years (almost 11 since we're a few days away from 2024). Many of the ecosystems you're holding up as the epitomes of stability have had their fare share of large scale changes in that same time frame. Remember go before generics, or Rust before async landed [1]? Elixir's Phoenix didn't even have its first commit when React was released [2].

> there is no equivalent of useContext for class components

Class components can still use context [3]. They obviously can't use the useContext hook, because class components aren't compatible with hooks, but that's kind of a tautology.

> you didn't have to use ReactHooks, but try using Apollo client or react-query without them now, or try finding any non-hooks documentation in general.

Library authors moved to hooks because sharing stateful logic was difficult before hooks. I never bought into the GraphQL hype train, but I remember there being a ton of verbose render props necessary to make GraphQL libraries like Apollo work before hooks. I don't think anyone really complained about data fetching libraries adopting hooks. You could argue that the react devs should have created the perfect library in 2013 complete with hooks, but I can forgive them for not having perfect foresight. Like I said, if an API changes occasionally over a decade I'm ok with it. Technology should be stable, not frozen.

[1] https://areweasyncyet.rs/

[2] https://groups.google.com/g/phoenix-talk/c/l8kIhc_LC7o

[3] https://legacy.reactjs.org/docs/context.html#classcontexttyp...

You can't use more than one context in React classes

"React" has been around for 10 years but in practice its been 3.5 different frameworks

- the original

- the class version (still close enough to original)

- hooks version (largely a whole different concept and "compatible" only superficially while the rest of the ecosystem starts writing incompatible things)

- server components, and `use`, which monkey patch fetch and already broke half of the ecosystem.

And no, hooks are still far from perfect and are infact very unintuitive and awkward to pretty much anyone who hasn't used React.

Additionally, there were designs possible to evolve from class components, its just that the React team cared more about

- "innovation",

- "functional" aesthetic preferences

- enabling a DCE compiler

rather than compatibility and continuity

Example design that would prioritise compatibility and continuity while still enabling all the current features of hooks: https://news.ycombinator.com/item?id=35192312

Again, this is _not enough_ attention to backward compatibility and stability in other ecosystems, even older ones like Python.

The problem isn't just react though. Most libraries in JS land just don't prioritise interfaces, compatibility and colaboration, instead opting for a "I'm just gonna build my own thing and not care about anything else out there" mentality.

Lets look at that Rust comparison and see how the equivalent situation looks like for JS. Before async-await, Rust had a variety of libraries implementing something close to Futures. They all largely standardized on the Future trait from the futures crate, which made it possible to write compatible library code that could build on top of any of them. There was a bit of an issue with compatibility as the trait essentially moved into std and some compatibility shims were needed, but after that a lot of the ecosystem adopted it and its possible to write runtime-independent code

In contrast in JS land, we had callbacks which were not even values, Promises (which had a working group that actually cared about compatibility), monadic Futures, observables, thunks, event-based interfaces, a variety of libraries. This came to be dominated with node-style callbacks, and then what got standardized were promises which instantly made most of the library ecosystem incompatible with the standardized syntax.

Both ecosystem have had these challenges, but to me it seems clear that the Rust community approached it with more care and thought on average.

Going beyond async-await, we can see similar patterns in the rest of the ecosystem. We have:

- half a dozen different component frameworks and not one has thought to define an compatible component interface that anyone could implement.

- 10 different popular libraries for manipulating standard library collections, but not one common protocol or trait (other than iterable and concatspreadable) that would make them largely work with other custom collections (e.g. MobX reactive collections or similar)

- 5 different popular bundlers and 10 older ones, but not one common trait, interface or standard for writing bundler plugins

- at a certain point, we had 3 different ways to define monorepo workspaces between npm, pnpm and yarn

So for a variety of reasons we have a very "internally incompatible" ecosystem. You can barely get components to work together, and what works together today is almost guaranteed to break tomorrow.

I think that if we recognize this is a problem collectively, there may be a way out. There is very high interest in some maintainers for doing this, especially those that care about compatibility. This is because one-sided care is not enough - the side you want to be compatible with has to care and prioritize it too. Those maintainers could decide to form a sub-community that prioritizes compatibility and continuity amongst them, as well as a promise to their users.

The recognition has to be more widespread than just in maintainers though, as frameworks that have done this before (e.g. Ember) have been largely left behind due to hype propping up the latest shiniest thing.

> "React" has been around for 10 years but in practice its been 3.5 different frameworks

> the class version (still close enough to original)

React.createClass existed because React is so old that not every browser supported native classes, and it was ditched when browser support improved. It was a relatively seamless transition. The rest of your bullet points are basically restating what we've already discussed. There have been two major changes in the past decade: hooks and server components. Again, I'm fine with two changes over the course of more than a decade.

To give you an example of a Go project that is roughly the same age as React and has introduced major breaking changes take a look at InfluxDB. It went from using SQL and InfluxQL as a query language in v1, to Flux in v2 as the headlining feature, then back to SQL and InfluxQL in version 3 recently [0].

> You can't use more than one context in React classes

You can, it just requires render props which are verbose (as I've already stated). There's a whole section of the legacy docs on this. This was one of the many motivations for the creation of hooks.

> 10 different popular libraries for manipulating standard library collections

Not really. Lodash is obviously the standard choice, and at one point Underscore had a bit of a following too. We wouldn't need these libraries if TC39 didn't drag its feet implementing the standard library, but browser technology has many stakeholders so it's a slow process.

> In contrast in JS land, we had callbacks which were not even values, Promises (which had a working group that actually cared about compatibility), monadic Futures, observables, thunks, event-based interfaces, a variety of libraries. This came to be dominated with node-style callbacks, and then what got standardized were promises which instantly made most of the library ecosystem incompatible with the standardized syntax.

I disagree with your characterization of async in JS. First of all you're tossing in a bunch of fringe concurrency techniques to distort history. There have been three mainstream ways of doing concurrency in the three or so decades that we've had JavaScript (in the following order). Callbacks (which is the primitive we were originally given from the browser and node emulated, e.g. addEventListener), promises (which have been standardized incredibly well, e.g. Bluebird promises still work after all these years), and async/await syntax which is a nice layer of syntactic sugar on top of promises that every language with promises eventually adopts.

Functional style monadic futures were never a mainstream way of doing things, to the point where people proposing them were told that they were living in fantasy land (which is why they literally created a library called fantasy land). Observables and thunks are not specific to JavaScript. RxJS (the most popular library for handling observables) has analogues in every popular programming language, and Reactive Extensions were actually originally created by Microsoft for C#.

> Example design that would prioritize compatibility and continuity while still enabling all the current features of hooks:

This is an HN comment you wrote with a small snippet of code. I would hesitate to call it a comprehensive design without peer review. You certainly could've submitted this during the RFC on hooks. There was a lengthy discussion before the design was finalized (which another commenter mentioned when you posted that comment to HN).

> Again, this is _not enough_ attention to backward compatibility and stability in other ecosystems, even older ones like Python.

Your definition of stability must be wildly different from mine then. Every time I have to install a python library on a new machine I audibly groan. There's a great article about this, and an associated discussion on HN and lobste.rs that I recommend [1][2][3]. Python is complex enough that it literally drove the adoption of Docker, because it was easier to ship an entire containerized OS than to instruct people on how to install Python packages.

This conversation is getting a bit lengthy, but I'll leave you with this:

- The most popular programming language on earth will inevitably explore the design space a lot, because there are so many people working with the language and thinking about these problems. As Bjarne Stroustrup said "There are only two kinds of languages: the ones people complain about and the ones nobody uses".

- Older languages will inevitably have more cruft than newer ones, because design decisions tend to accrete over time.

- JS is in a particularly difficult position because it runs in both the browser and the server, and because it's built on web standards and TC39 uses design by committee (there's no BDFL supervising JS).

[0] https://news.ycombinator.com/item?id=37614611

[1] https://www.bitecode.dev/p/why-not-tell-people-to-simply-use

[2] https://lobste.rs/s/vtghvu/why_not_tell_people_simply_use_py...

[3] https://news.ycombinator.com/item?id=36308241

One last bit

> You certainly could've submitted this during the RFC on hooks. There was a lengthy discussion before the design was finalized (which another commenter mentioned when you posted that comment to HN).

There were RFCs submitted. My conclusion from that entire discussion was that the React team proceeded the way they've decided anyway without providing very convincing explanations and the priorities they had didn't align with most of the community. I believe this is in line with how other Facebook OSS works. React has largely been built with internal clients and their needs in mind. Additionally there seemed to be the desire to continue to be "iconclastic" and innovative and power through criticism in a similar manner that the initial React release did.

That's all perfectly fine, but in my opinion its not what the community needs, and we'de be better off moving on to other solutions (e.g. Solid, Svelte). But more important than that, we need to move on to a different mindset, because without that mindset change we're likely to go through the same problems over and over again.

There are very few projects, in Javascript or elsewhere, that care about backwards compatibility the way React does.

You can still easily put a class-based component inside your modern hook-based codebase, and it will work (I did it this year :) )

But I think they kinda dripped the ball with Suspense and server components

> I disagree with your characterization of async in JS. First of all you're tossing in a bunch of fringe concurrency techniques to distort history. There have been three mainstream ways of doing concurrency in the three or so decades that we've had JavaScript (in the following order). Callbacks (which is the primitive we were originally given from the browser and node emulated, e.g. addEventListener), promises (which have been standardized incredibly well, e.g. Bluebird promises still work after all these years), and async/await syntax which is a nice layer of syntactic sugar on top of promises that every language with promises eventually adopts.

I am (was) one of the maintainers of Bluebird. I'm well aware of the exact state of the node-dominated ecosystem at the time. Promises weren't much more popular than the "fringe" libraries you're describing, and (err, res) style callbacks largely dominated in the ecosystem. And yet it didn't matter - promises got standardized, even though the node community largely disliked them. Very different from the Rust process.

> Your definition of stability must be wildly different from mine then. Every time I have to install a python library on a new machine I audibly groan.

I'm aware that the python packaging story sucks. However there is broad awareness and strong desire to fix this. There is standardization work to get the ecosystem into a state that is largely coherent. There is much less such desire in JS: the thought of coming up with a "standard component interface" for example (i.e. a cross-framework compatible way of writing components) would be met with ridicule. (Note that I don't mean web components - I mean a common interface in the "trait" sense)

The Python article you linked itself admits "expirienced people will know what to do, this is not for them". Well that's not the case in JS - its actually really easy to get a combination of tools that simply will never, ever work with each other, just because they weren't designed with any common interface or compatibility in mind - regardles of how experienced you are. For example, at some (relatively short) point in time around 2019/2020, react-native simply didn't work with pnpm, because it assumed that packages installed together will have access to the same node_modules, so some of the packages dind't have the correct dependencies listed; pnpm being strict dind't provide the unlisted ones, which mean react-native was broken. (Later pnpm added a mode just for this sort of brokenness, but these sort of limitations pop up all the time)

> JS is in a particularly difficult position because it runs in both the browser and the server, and because it's built on web standards and TC39 uses design by committee (there's no BDFL supervising JS).

There's been plenty of time to fix things. Its not just about the ability, its about the general attitude in the community and our priorities. Before things get better, we have to admit they're really bad, and that we need to focus more on compatibility, continuity and colaboration instead of ignoring them in the pursuit of the latest "innovative" idea.