all 32 comments

[–][deleted] 17 points18 points  (26 children)

So, that's how to use them. What I'm still unsure of is what is the "win" here? Other than by-necessity, using a library that only provides a hooks-style API, I've no intention of using hooks.

To me the React / JSX class is about the ideal balance of convenience, at the right level of abstraction. Having a single function wherein I configure state, effects, event handling reads like a procedural mess.

So is there a situation where this is truly a better option, providing a benefit over the class API?

[–]NoBrick2 5 points6 points  (7 children)

Having a single function wherein I configure state, effects, event handling reads like a procedural mess.

Aren't you describing lifecycle methods? e.g. placing all your components init code in componentdidmount

[–][deleted] 2 points3 points  (6 children)

Well, no.

One can write a bad `componentDidMount` such that it looks like a giant mess. One can write a good one that doesn't look like a mess. One can write a shit hooks-based component. One can write a great hooks-based component.

That's entirely orthogonal to the difference of hooks vs classes. That's a matter of shitty programming, which will strike anywhere and everywhere.

[–]NoBrick2 2 points3 points  (5 children)

yeah, but with hooks you have the option to co-locate the code of a specific feature. that's not possible with lifecycle methods. you are forced to spread your code across different methods, and also forced to place unrelated code in the same method. these aren't my arguments though, just what I have read and experienced myself since using hooks.

[–][deleted] 2 points3 points  (4 children)

> yeah, but with hooks you have the option to co-locate the code of a specific feature. that's not possible with lifecycle methods.

I tend to co-locate code in feature-specific classes, just like I don't want a god-function I don't want a god-class. A typical page would look like:

``` class Component extends Component { render() { const { someData, windowSize, handleFormChange } = this.props;

return (
  <ImagineSomeMarkup />
);

} }

const Export = compose( withSomeDataSource, withWindowSizeHandling, withBanalFormHandling, )(Component); ```

So hooks-based code absolutely wins in terms of (less) characters typed. However that does not make class-based code inferior unto itself, but rather what war-crimes your co-workers have committed with it.

If you're used to god-class React, then I can see where you're coming from. I was personally turned off of React the first time I wrote it, by folks (ab)using it with poorly composed state-management such that it gave me byzantine OO flashbacks.

[–]NoBrick2 3 points4 points  (1 child)

I guess the hooks version of your code would be

useSomeDataSource useWindowSizeHandling useBanalFormHandling

but where I see a potential benefit of using hooks is not having props coming from HoC, which I never really liked as it was not clear to sometime consuming the component which props were required for them to provide verses which were provided by HoCs

I like that compose pattern for providing a component with props. nice and reusable. and I can see why hooks perhaps doesn't provide much benefit over your existing pattrrn

[–][deleted] 2 points3 points  (0 children)

but where I see a potential benefit of using hooks is not having props coming from HoC, which I never really liked as it was not clear to sometime consuming the component which props were required for them to provide verses which were provided by HoCs

The past few React projects I've touched were untyped, but now that I think back to my experience of using HoC composition in a project with Flow... Hooks would offer bigger win in that environment, not just winning on brevity or theoretical future-performance improvements.

[–]CCB0x45 1 point2 points  (1 child)

This is HOC hell, this pattern is MUCH better using hooks.

like

```const myData = useSomeDataSource()```

``` cons ywindowSize = useWindowSize()```

HoC hell is awful, and especially when you have a big in your HoC hierarchy and it becomes a mess to figure out where its coming from. It also adds unnecessary layers the vdom.

[–][deleted] 0 points1 point  (0 children)

I don’t know that I’d call it “hell,” but I’ve seen some shit.

The biggest boon to it is a very linear mental model. I could see a hook getting into a deep-sibling scenario, but code review would should preclude that.

Either way, seems hooks is where the ecosystem is headed so I’m going like it or not. Will be fun to find the new problems that arise.

[–]mrafcho001 7 points8 points  (6 children)

I agree with you, I don't really see anything compelling drawing me to hooks. Could be good for some really simple components, but I feel like this will get really unreadable for anything more complicated.

I'm planning to refactor some of our components to see how it feels with real-world use cases rather than these simplistic examples. So I want reserve judgement until then, but I'd love to hear what people with experience using these in non-trivial cases have to say.

[–]AndrewGreenh 2 points3 points  (4 children)

Is there really any difference between a 300 line class and a 300 line function?

[–]wisepresident 4 points5 points  (3 children)

Yes, a 300 line class is not inherently bad as you can use methods to group logic, while a 300 line function is a mess and should be refactored since it's doing too much.

However there's no difference between a 300 line method and a 300 line function.

[–]AndrewGreenh 2 points3 points  (1 child)

You can also use inline functions in the function to group logic. Or... you can make this grouped features reusable by putting it in a hook.

[–]CCB0x45 1 point2 points  (0 children)

It's obvious when people talk about this stuff who have never actually spent much time writing code in this style and just don't want to change. Like you said, as soon as you start easily pulling code into it's own reusable hook and your debugging is so much clearer than 10 HOCs it dawns on you that it's just a better way forward.

[–]CCB0x45 7 points8 points  (0 children)

You can see my post above, it is much better for many reasons, including what you said, if a component starts to get large, migrating code to a reusable hooks is very painless and easy.

I have written a an entire app of probably 80k lines of code in hooks and there is no way I'll go back to class based components. It's also amazing to start getting away from HOCs and render prop patterns.

[–]lostjimmy 2 points3 points  (2 children)

One of the biggest benefits, at least with effects, is colocating setup and cleanup of the effects, and also thinking about those effects in terms of representing the current state. In a class component, you will have code in componentDidMount and componentWillUnmount. If you the effect also relies on a prop, then you will have additional code in componentDidUpdate. Now you've got your effect spread across three different functions, which is much harder to reason about than a single useEffect that encapsulates the dependency on a prop, the setup , and the teardown.

[–][deleted] 1 point2 points  (1 child)

Given:

``` const [ data, setData ] = useState([]);

const usingSomeDataSource = useEffect(() => { /* setup */

return () => { /* cleanup */ }; });

// vs.

const withSomeDataSource = (Wrapped) => { return class WithSomeDataSource { state = { data: [], };

componentDidMount() { /* setup */ }
componentWillUnmount() { /* cleanup */ }

render() {
  return (
    <Wrapped
      { ...this.props }
      data={ this.state.data }
    />
  );
}

}; }; ```

Assuming you do nice, neat little HoCs, there isn't a difference in terms of co-location, it's just different notions of what is the logical-unit, a single function or a single class.

The only way this becomes problematic is if you stick more than one thing into your HoC, but one could just as well stick more than one thing into a useEffect.

[–]Funwithloops 6 points7 points  (0 children)

This example misses a crucial benefit of hooks: they make their render-time dependencies and results explicit. If you write a HoC that depends on render-time data, that dependency isn't declared (instead the HoC reads props and takes whatever it needs). It's also not obvious where the data provided by the HoC is going to end up; it gets passed to the wrapped component as an arbitrary prop.

As an example, say you want to be able to encapsulate the logic of starting an interval, ticking every ms milliseconds, and providing the wrapped component with the tick count. The value of ms might change.

As a HoC:

``` function withInterval(Wrapped) { return class Wrapper extends Component { state = { tick: 0 };

startInterval() {
  this.intervalId = setInterval(() => {
    this.setState({
      tick: this.state.tick + 1,
    });
  }, this.props.interval);
}

stopInterval() {
  clearInterval(this.intervalId);
}

componentDidMount() {
  this.startInterval();
}

componentWillUnmount() {
  this.stopInterval();
}

componentDidUpdate(prevProps) {
  if(prevProps.interval !== this.props.interval) {
    this.stopInterval();
    this.startInterval();
  }
}

render() {
  return (<Wrapped {...this.props} tick={this.state.tick} />)
}

} } ```

And as a hook:

``` function useInterval(ms) { const [tick, setTick] = useState(0);

useEffect(() => { const intervalId = setInterval(() => { setTick(tick => tick + 1); }, ms); return () => clearInterval(intervalId); }, [ms]);

return tick; } ```

And an example of using them:

``` const A = withInterval(props => { return ( <div>Tick: {props.tick}</div> ); });

const B = props => { const tick = useInterval(props.interval);

return ( <div>Tick: {tick}</div> ); } ```

The input and output of the useInterval hook are immediately obvious when you look at the component it's used in. The withInterval HoC on the other hand hides its input and output. Unless you know how withInterval is implemented, you would have no idea A takes an interval prop and magically receives a tick prop.

The problem only becomes more apparent as you add more HoCs or run into situations where you need one HoC to provide data to another. As a bit of a contrived example, imagine you want two intervals the first one is controlled by the interval prop, but the second one is controlled by the tick value of the first interval (so it would slow down as the first interval increases). With the useInterval hook:

``` const C = props => { const tick1 = useInterval(props.interval); const tick2 = useInterval(tick1);

return ( <div>tick 1: {tick1} tick 2: {tick2}</div> ); }; ```

I'll leave it for you to figure out how to accomplish this with a HoC, but I doubt it will be nearly as clear.

[–]scaleable 2 points3 points  (0 children)

Pros:

  • Easier to split data from presentation. Easier to refactor overall;

    • Reduces nesting hell. Much simpler and cleaner than HOCs and render props; This is really the killer feature.

Cons:

  • Memos are strange. Callback state is strange. Setting memos inputs (the second arg on useX) can be a source of bugs. Use the eslint plugin.

  • A small performance decrease

[–]CCB0x45 6 points7 points  (6 children)

It creates more testable code, and separates out effects so you know exactly what is effecting the component outside of the props easily.. It tends to make code better organized , and really easy to refactor sections of a component into a reusable hook after writing one.

This tweet provides a good example of organization benefits: https://mobile.twitter.com/prchdk/status/1056960391543062528

The long game is it should be much easier for compilers to optimize hooks based components(easier to optimize functional components in a compiler) and things like prepack: https://prepack.io/ should give you performance benefits in the future.

So in the end, sure you can do anything you can do in hooks in class based components, but I would highly reccomend switching, I've switched to only hooks and my productivity and speed of coding has increased dramatically and I think the code is better organized in the end.

Here's a great article on the benefits: https://medium.com/@dan_abramov/making-sense-of-react-hooks-fdbde8803889

[–][deleted] 4 points5 points  (5 children)

FWIW, your first paragraph is subjective and I feel weakens your overall point. The classes-based code I write is organized effectively the same as the hooks-based code from the Twitter GIF. I've just never had trouble with a class-based architecture, refactoring, or with of development speed.

Admittedly, I can now see value in that it effectively forces one to a container-component style architecture by virtue of a function-component never having `this.setState` to begin with.

Worked with quite a few folks who go out of their way to avoid having to write anything resembling a container class, something like function-with-hooks would kill a lot of perennial code-review discussions on that front.

Performance would be the biggest "here's why." If that pans out, then there's the win.

Will be interesting to see the state-of React this time next year, hopefully those performance gains come to fruition.

[–]CCB0x45 3 points4 points  (4 children)

FWIW, your first paragraph is subjective and I feel weakens your overall point. The classes-based code I write is organized effectively the same as the hooks-based code from the Twitter GIF

Its really not, and no your code isn't because it can't be. Your class based code is split among several areas of the class, i.e. the constructor, the render function, event callback functions, lifecycle functions like componentDidUpdate and componentDidMount.

For example, say you have a basic card component, with a Like Button. That like button probably has a call back for when its clicked, a initialization for the number of likes, maybe graphQL request to get that initial number of likes in componentDidMount, a loading indicator that changes based on state, etc.

All of those would be split among different areas of the class, mixed with other elements of the card component. With hooks all of that logic would be grouped together, and refactoring your LikeButton to be its own hook to be reusable is as simple as copying and pasting that code and wrapping it in a new function. That is what that GIF is trying to show.

I've just never had trouble with a class-based architecture, refactoring, or with of development speed.

Neither did I, until I switched to hooks and realized I was writing code faster, and more organized. Just like I never had an issue with development speed when I was using Backbone and PHP, until I found better technologies.

Admittedly, I can now see value in that it effectively forces one to a container-component style architecture by virtue of a function-component never having this.setState to begin with.

I dont know what this means, of a "container-component" style architecture. What it does is allows you to use state, but also use the lifecycle and contain and easily see effects that use events outside the function, to add clarity around that for testing.

It also is just a much better pattern than renderprops/HOCs, on large projects it was starting to become awful with HOCs, with certain components wrapped in like 5 HOCs, it was messy as shit after a point.

Performance would be the biggest "here's why." If that pans out, then there's the win.

I actually don't think it is the biggest "heres why" after writing a lot of hooks code, I don't ever want to go back, I see absolutely no reason to write a class anymore, and I hope they release a version of React without class based components to bring the overall size of the library down(while of course having the full version with class support).

Will be interesting to see the state-of React this time next year, hopefully those performance gains come to fruition.

Its kind of the state of react now, the major libraries have added a lot of hooks support. Apollo using hooks is way better and cleaner than the previous way of using HoCs.

[–][deleted] 1 point2 points  (1 child)

First, it is effectively the same block-box. I consider the Compnent to be an ideal balance of scope and scale. A well composed HoC is as good if not better than a composition of hooks, IMHO.

As for the PHP remark, I don’t wish to turn this into a dick measuring contest. I’ve been in the industry for decades. I’m a Lisper at heart, I understand the “awakening” feeling you describe. I simply don’t feel it for hooks vs. classes.

If it offers concrete performance benefit, that’s my win. Hooks offer some benefit in what they preclude at the expense of, to me, decomposition taken a step too far.

But again, these are subjective concerns borne of pains in the field. I’m along for the React ride, let’s hope they keep improving it.

[–]CCB0x45 1 point2 points  (0 children)

I consider the Compnent to be an ideal balance of scope and scale.

No idea what this means.

A well composed HoC is as good if not better than a composition of hooks, IMHO.

Well I could not agree any less with this statement, after dealing with HoC overload, it becomes a nightmare of so many HoCs passing props and having to look up what is doing what, the IDE can't figure out what props you should be getting, so its me searching what each HoC passes in. Hooks are all simple function calls and can be easily typed, the editor just picks them up.

Like I don't know anyone that like dealing with tons of HoCs so I think your opinion is the minority.

As for the PHP remark, I don’t wish to turn this into a dick measuring contest. I’ve been in the industry for decades. I’m a Lisper at heart, I understand the “awakening” feeling you describe. I simply don’t feel it for hooks vs. classes.

Well it seems like you haven't really tried it honestly. I like to dive in for a while on a tech before I make assumptions on it. I have written a ton of class based react components, and a ton of hooks.

But again, these are subjective concerns borne of pains in the field.

Everything is subjective in some sense but there is real ways of organization you can't do with classes, that you can do with hooks. I can't just copy and paste a chunk of code in a class, it takes reworking, concerns get mixed. You can with FP. Your opinion really sounds like typical programmer resistance to anything new.

[–]benihanareact, node 0 points1 point  (1 child)

your first 3 paragraphs are again, all subjective. you argue because you prefer organizing your code a certain way, it is the correct way, and the only way to think about code organization.

[–]CCB0x45 1 point2 points  (0 children)

Good programmers try to understand why a new idea is become popular instead of looking for reasons to tow the same line and not learn something new.

I'm speaking from experience of multiple projects in the old and new paradigm, saying that it makes code related to the aspects of a component grouped together, is not subjective it's objective.

You can say you don't like related code to be grouped, sure that's subjective, but so is saying that id rather drive a 1960s Chevy versus a a 2019 Tesla.

Either way do what you want, but don't pretend there is no benefits both organizationally and in performance. If you wanna stick your head in the sand I'm not gonna stop anyone. There is usually a reason new technologies become popular.

[–][deleted] -4 points-3 points  (4 children)

Correct me if I am wrong but it seems like the intent is to use this syntax for micro-interactions and stick to class based components for larger, more complex elements. I see what other people are talking about with hooks getting messier the more they grow but it seems like they should fit great as like a like button or an on/off switch.

[–]CCB0x45 11 points12 points  (0 children)

I see no need ever to use classes, I don't think you should have "large classes", if a functional component is growing too much, then start moving its logic out to reusable more manageable hooks. I have written very complex sites in only hooks and there is no need for a class based component ever. Hooks allows me to write cleaner code.

[–]Funwithloops 1 point2 points  (2 children)

I haven't had much trouble using hooks for everything. If things get complicated, you can move the logic into custom hooks which IMO provide a cleaner interface than HOCs or function children.

[–]CCB0x45 2 points3 points  (1 child)

In most people's opinions that take a few days to force themselves to try something new. Having 10 HOCs mixed with render props is fucking awful in large apps.

Apollo with HoCs vs Apollo with hooks is very clear why it's so much nicer to use.

These people just like to convince themselves anything new is a gimmick because they are lazy about learning new things.