Hacker News new | ask | show | jobs
by sadturnip 2793 days ago
> In our observation, classes are the biggest barrier to learning React.

As someone who struggled hard with some aspects of learning react, i felt this to be the absolute opposite. Watching people to combine and spread logic over dozens of functional components, and drag in other external libraries like recompose to do stuff like lifecycle hooks, and using HoC's, just to avoid classes makes my head hurt.

> Only call Hooks at the top level. Don’t call Hooks inside loops, conditions, or nested functions.

I really don't like this, and to me this feels really finicky, and unfortunately the explanation doesn't make me feel warm and fuzzy ether. While it is great they are adding a linter plugin for it, i feel like this is going to be really easy to shoot yourself in the foot, and feels like it is relying on behind the scenes magic too much.

10 comments

You also can call functions that call functions that set up hooks. What if those functions have control flow? Must we build linters that recursively taint any hook-touching code segments?

Since Fiber, React has become difficult to reason about. There is a stateful crawler that is going up and down and sideways in your hierarchy, running code with side effects and telling you that it will isolate those side effects. The golden brick road is surrounded by briars, because you cannot know the nuances of that renderer’s contracts, because you cannot read the code of that renderer, because it’s too dense to understand. I miss the days when React was a simpler “dive” than Angular in this regard, and I could boast about its simplicity. In the interest of efficiency, we have forgotten how to climb the walls of our garden.

>> React has become difficult to reason about

Absolutely. React started off simple but with things like Redux added on, it has become unnecessarily complex. The performance benefits of React is overrated. Javascript is so fast these days that you can rerender the whole page on navigation and no one will know the difference. I built a whole social network site based on a simpler alternative, UIBuilder, and you can see for yourself that it is performant: https://circles.app

UIBuilder is here: https://github.com/wisercoder/uibuilder

But they needed Fiber to make AsyncMode possible

And once it's well baked enough, it works very cleanly with universal rendering strategies. It means you don't have to separate logic into components where it doesn't belong because the server render ignores loading logic in componentDidMount. It's suddenly all first class

And, then, theoretically you can remove Redux and just use Context

It has gotten really wacky along the way. I hope they don't lose sight

I feel the same way! Maybe I'm just getting older, but I like code to be boring. A class is something everyone can read and understand. If you have to navigate a maze of HOCs withStateHandlers, enchancers, redux actions, reducers and connectors you end up needing to open 10-20 files and jump around between as many functions to build a picture of how a component works. Considering code is read more often than written - that seems like a huge step backwards.

Hi, I'm Dan. I write boring code. I love Go because it's super boring.

I like to read boring code, write boring code, and then get on with my day. I don't like long walks through the codebase trying to understand how everything is wired up in a super-cool functional way. Get off my lawn kids.

What's actually funny here, is that maybe you aren't old enough. The "Super-cool" functional way has been around as long as types and functions, Lisp and ML have been around since the what...50's and 70's respectively. Immutable state and referential transparency are boring. Get off MY LAWN kid!
OMG. I am Dan also. I am getting older also. I LOVE Go also! Too funny but we are living parallel lives. Now get off MY lawn!
functional javascript can increase code clarity if written well. Certainly ramda is usually a huge improvement when making mutations in a reducer. compose()'ing a bunch of HOCs is essentially the same as having mixins (and it's exactly the same as @decorators), just with a slightly different syntax.

I think it's mostly down to the paradigm you're most familiar with. Classes are normal to OO developers, functional composition is normal to FP developers.

Mmm pointers.
Amen!
> A class is something everyone can read and understand.

This is simply not true. Your post is very strange, you just assume classes are simple and boring but everything else is 10-20 files with fancy magic and blah blah blah.

The truth is that Dan is wrong about what makes code boring. Immutability is boring. Dan just likes Go.

A class has all the related code in one place in one file (some languages excepted, base classes excepted, but true enough for React purposes where inheritance hierarchies are quite limited and rare in my experience.)

I like immutability. It can make code easier to read and understand if done well. But within reason.

It's not that I'm creating some strawman where the alternative to a class is 10-20 functions across as many files - but that's actually mild compared to some things I've really seen.

To give an example, about a month ago I had to read a co-workers implementation of a feature and it was literally a maze of some 30 functions many of which returning closures, some of which return other closures. With state scattered all over in withStateHandlers, etc. I spent two hours on a Saturday trying to understand it and make the changes I needed to make before I gave up and assigned the ticket to my coworker who wrote it all. Basically he's the only one now who can modify that code easily (and incidentally he's away now on leave, if we have to modify that code again, we're screwed.)

So immutability of data is awesome. Immutability of code is not. If it was a couple boring classes I could have read the code and made all the changes I needed to make in less than an hour.

Simple an predictable and boring is fantastic where code is concerned. I love Go not because of the features it has, but specifically because it's limited enough that the code written in it is almost always easy to understand.

React is intended to be a view-only library but in practice people shove all their business logic in there, mostly because React makes it incredibly hard to do anything else. Because of this, innovations in React are going in the wrong direction. This Hooks feature is meant to solve a portion of this, but it's going in the wrong direction.

A better bigger-picture innovation would be to have a React-alike that is actually only the view layer, and makes it significantly easier to write your business logic using plain old JavaScript functions/classes.

Ideally it would have an API that encourages you to write your business logic as a hierarchy of state machines, because that's what apps really are. But the state management should definitely be done outside React, similar to what Redux has done, where your actual state is transformed into props and passed into React.

Also, screw you HN for always penalizing my account so that my content shows up at the bottom instead of the top and has no chance of being seen. My content is good, your algorithms suck.

> React is intended to be a view-only library but in practice people shove all their business logic in there, mostly because React makes it incredibly hard to do anything else. Because of this, innovations in React are going in the wrong direction. This Hooks feature is meant to solve a portion of this, but it's going in the wrong direction. A better bigger-picture innovation would be to have a React-alike that is actually only the view layer, and makes it significantly easier to write your business logic using plain old JavaScript functions/classes.

This. This is spot-on IMHO.

You can literally use React for JUST the view layer... you don't have to maintain any state inside react if you don't want to. Early on, many did use it for just a view layer.
> in practice people shove all their business logic in there, mostly because React makes it incredibly hard to do anything else.

I'm not an expert by any means, but I wrote a fairly substantial react app recently, and I found that redux not only made this straightforward, but made it hard to do anything else. I did have to learn redux and redux- saga on top of react, but it wasn't hard.

Just a FUCK-TON of boilerplate :P
You say boilerplate, I say simple declarative layout of the boundaries between UI, state and state transitions. :-)
People use the word "boilerplate" a lot, but everyone seems to have something different in mind when they say it. What specific concerns do _you_ have? I'm a Redux maintainer, and I'm always happy to try to help offer possible solutions.
I don’t really think there is a way around it if you go with the action/reducer/saga model.

I enjoy my code fairly explicit, so writing a button click that invokes some request has me writing:

- A click handler

- 3 new action name consts (request, success, failure)

- An action instantiation function that takes the necessary arguments for request.

- A saga function that catches these request actions, runs a request, and invokes the success action/failure action depending

- 3 handlers in my reducers. One for every action.

It all adds up if you have a ton of buttons :P

Sagas are a great power tool, but I personally wouldn't use them for most simple API requests, largely because of the need for "signal actions" to trigger the logic.

You might want to check out our new `redux-starter-kit` package [0], which can simplify some common use cases for things like action creators and reducers. There's also many other existing libraries to handle repetitive code like API requests as well [1].

[0] https://github.com/reduxjs/redux-starter-kit

[1] https://github.com/markerikson/redux-ecosystem-links/blob/ma... , https://github.com/markerikson/redux-ecosystem-links/blob/ma...

This is exactly the thing we need fixed. I get that Angular has one way of doing it, but I still feel like it's going in the wrong direction (too Java-like).
I agree. I feel using JSX for example to define application routes (a la react-router) or do any other type of non view logic is just wrong.
Well, you usually have more than one layer of state... a holistic application state, and a control/component state.

It's possible to completely separate out your state machine and use it separately from rendering... It's also possible to integrate it all. Best practices are generally somewhere in between and having your state where needed... if it can be contained, contain it... if multiple controls need it, globalize/context it.

React was never a view-only library.

Pete Hunt always said he saw React components as mini MVC modules.

it's even less true nowadays with features like Context or suspense to supplement the component states. Redux was kind of a temporary hack.

I distinctly remember the phrase "V in MVC" being prominently displayed somewhere on the official React website(s).

Found it: https://web.archive.org/web/20140329114924/http://facebook.g...

> JUST THE UI

> Lots of people use React as the V in MVC. Since React makes no assumptions about the rest of your technology stack, it's easy to try it out on a small feature in an existing project.

So I was mistaken. It didn't say that's what it's only intended for, but that's what many people use it for.

Either way, it's not great at the M or C parts of MVC. Context does very little to ease that pain. Redux was more manageable but also overly complex, but it's only React's design that forced it to be so complex.

But managing state is quite hard in general. Angular proudly advertises it solves all problems you can have on the frontend but I would take react state + utils over angular's magic templates, rxjs and bindings any day.
Right, and that's what I'm recommending we in general try to innovate on: better state management than React, but with the ease of writing view components that React has showed works well. Since the view is hierarchical, it makes sense for components to inherently also be hierarchical, but business logic often doesn't match up to React's component hierarchy, so it should not be done inside React or tied to React's hierarchy.
Out of curiosity, have you actually tried the latest version of Angular to see if your preference does hold up? It’s come quite a way since the bad old days.
I can't speak for GP, but have used angular up through 4.x (not quite current) and don't find it better than React's ecosystem imho.

With React I can often bring in additional components and libraries without friction. With Angular it feels like doing anything means significant friction. And heaven help you if you want to change the way something internal works, or work around things.

I agree. Optimizing a developer library for developers who struggle to understand what a class is seems like a good way to build something very convoluted.
I don't think the issue is "what is a class". It's more that the lifecycle functions get tied into the class, and you end up with a ball of highly conditional logic. It's easy for newbies to just add stuff to make it work, but end up with a mess.

React is the view layer, so the class concept of "here's some data and methods to alter it" doesn't really match with what happens - I don't know that components every should have been classes in the first place.

According to the article yes, the issue is "what is a class":

"You have to understand how this works in JavaScript, which is very different from how it works in most languages. You have to remember to bind the event handlers." etc.

Eh, even in the same paragraph it says "The distinction between function and class components in React and when to use each one leads to disagreements even between experienced React developers."

Also, bits of class issues show up in the other complaints.

So, no, it's not just "what is a class" and it's a weird hill to die on. I feel like most of these responses to hooks in general just focus on the most trivial detail aka bikeshedding.

I'm focusing on classes, because it seems to me that to "React" classes are a necessary evil (or they are treating them like they were).

Hooks, suspense, context wormholes - I'm not sure if these features should be part of a view engine. React, to me, did one thing and did it well, but now... I don't know.

Can't argue with the text of the article. I do, however, stand by why _I_ have found that classes are a bad match for this need, and watching the video of the demo did a much better job of suggesting the benefits and reasoning...reasons that don't really match the parts of the article you've quoted.

https://www.youtube.com/watch?v=kz3nVya45uQ&t=39m (from elsewhere in the comments here)

>Video unavailable. This video contains content from WebTVAsia (Music), who has blocked it on copyright grounds.

LOL, did they just block a video presentation for some incidental random music in the background?

It's not just having to understand what a class is, it's also having to know and remember to add a constructor to your class just to call `this.handleSomeEvent = this.handleSomeEvent.bind(this)`.

Sure, I know what it does and why it is needed, but it is ugly, cumbersome, and I keep forgetting it nonetheless.

There are other ways to approach an issue beyond shoving everything into a class. I'm not sure I really like the new hooks over the state methods at that level since I tend to separate global state (redux) vs component state (class) vs component state rendering (functional component).

In the end it depends on your style. I think this might be useful in a way that can be more pure than some other methods might be.

I don't know what you mean by pure. There is nothing reverentially transparent about the new API. (Nor can there be, really.)
And nothing that requires you to use the new API.
I can see both sides. I teach React to newish coders, and classes are easy for them to grasp...and then immediately create labyrinthine monoliths. At work where I do React, we emphasize small components with limited and segmented logic (ideally pulling as much logic out of the React parts as possible - and this is most easily done by avoiding classes).

At the same time, I've a passion for trying to make code more maintainable - which often means avoiding too much abstraction and keeping logic plain and up-front, where it can be found and followed, so I share some of your concerns.

But I'm excited about this. One of the best parts of React has been that the same logic that makes a good program makes for a good react app. Treat your components like functions (regardless of whether they are classes or not) - small, single purpose, decoupled from state - and you'll have an easier time. Hooks look to help that.

I can see both sides. I teach React to newish coders, and classes are easy for them to grasp...and then immediately create labyrinthine monoliths.

To be fair, that's pretty much what happens to all newish coders who are learning classes. Learning to use classes responsibly is really just part of the learning, though that seems to be the part that instructors pawn off to the next guy.

Once you learn how to use them judiciously, classes become extremely useful tools for encapsulation/abstraction where you need encapsulation/abstraction.

Maybe newish coders should learn the language they are using.

And of course there is typescript (and a gazillion of languages that can be transpiled to js these days), which synergizes very well with enterprise people and java/dotnet devs who never did a line of frontend before.

Maybe newish coders should learn the language they are using.

It's not a language thing. OOP discipline is a coding thing in general.

And of course there is typescript (and a gazillion of languages that can be transpiled to js these days), which synergizes very well with enterprise people and java/dotnet devs who never did a line of frontend before.

You sound like you don't actually understand why people like Typescript, or, more specifically, static typing.

> You sound like you don't actually understand why people like Typescript, or, more specifically, static typing.

Eh, in my experience, there are two types of Typescript users: 1) those users who appreciate the safety that type systems provide when used properly, and 2) those enterprise users of the language who have mostly only coded Java and C# and who like Typescript because it lets them write Java-style code for the browser.

Typescript doesn't do anything ES6 & Webpack don't already do; Javascript already lets you write Java-style code if you really want to.
I "don't actually understand why people like Typescript", or lemonade, or skiing, or eating fish. I know why I like it, including static typing, and how I can find a balance with using its features (and not using some).

On the other hand I'm not trying to hide that I'm bitter about the mental lazyness around typescript - I had some bad experience with interviewers who praised ts (without ever bothering to learn the core principles of javascript) a bit too much for my taste (but again, this may just be dismissed as anecdotal evidence, which it is).

You wish we went back to bind and apply? And not being able to trace logic, state, and DOM through a labyrinth of Backbone views? We have come so far

How is static typing "mental laziness"?

> and using HoC's, just to avoid classes makes my head hurt

I don't know if it's just something I don't see that often and aren't that familiar with, but I also can't stand the overuse of HoC's for this purpose. Seen some very clever and very, very unreadable HoC's so far and I hate working on codebases that use them liberally.

I think it depends on the use... I think some can be ugly... right now react-loadable and a custom InRoles component are the two I'm using the most. One is for loading state and code-splitting, the other is for allow/deny state...

    <InRoles roles={[...]}
             allow={() => <Component ...props />}
             deny={'/route' /* or a render, or skip */}
    />
InRoles works for a router redirect, displaying alternate content or no content.

In the end it depends on what you're trying to accomplish. HoC's are pretty useful. Also, it really helps in terms of unit testing your more functional components.

I think the HoC's that get me the most are the ones written by JS devs who think 100% functionally. Actually a lot of the hard ones to grok were written by Jason Miller of the Preact fame, who seems to think out JS in a minified, functional fashion. I've had to step through and re-write several things just to see what was actually happening, then pare it back to it's original form before I "get it". It's super clever, but man did it take me a while to distill it's logic.
While I do agree that higher order components are not the most beginner friendly pattern, to state that they are being used just to avoid classes completely misses the points of why they are used. They are a functional pattern that help one create pure components.
Yup! they can be tested separately and introduce a separation of concerns that is much better than a "simple" class that "just has everything in it". (Which usually leads to mounds of copy-paste code)
(I work on React.) I think we’re on the same page here. Having a pile of recompose helpers and HOCs is exactly what we think/hope Hooks will help avoid.
> i felt this to be the absolute opposite

I completely agree.

When I started using React a couple of years ago, the only thing that made sense were the classes.

The bad aspect about React and JSX is that in general there are way too many JS acrobatics which alienates everyone from the codebase who isn't a JS ninja.

In my experience classes are one of the few boring, easily understood parts of react for almost everyone I mentor/work with.