Hacker News new | past | comments | ask | show | jobs | submit login

I've read a lot about hooks, the reasoning all makes sense but I'm still not fully getting it at an intuitive level. Like I get that it means you can write functional versions of class based components, an example of a complex class component that has been converted to a function with hooks would be good. The counter example really leaves a lot to the imagination.

The examples in https://usehooks.com/ aren't convincing me for some reason, but maybe that's just my lack of familiarity with the syntax so it feels more magic. Lifecycle methods like componentDidUpdate are super easy to reason about at first.




A major point of Hooks is that they’re composable — unlike lifecycle methods.

You can separate different reusable pieces of logic into Hooks, and then combine them or pass values between them. You can even call the same Hook more than once.

I’ve wrote about new possibilities they unlock here:

https://medium.com/@dan_abramov/making-sense-of-react-hooks-...

Hope this helps.


But why does Hooks need to be implemented on top of functions and not classes? The way I see it is that Hooks today implements two distinct things: (1) a composable way to handle lifecycle and (2) a custom way to store state. Why can't we have the composable hooks available on React.Component like this?

    class Foo extends React.Component {
       constructor(props) {
         super(props)

         this.useEffect(…);
         this.useState(…);

         // or maybe even a top-level API:
         useState(this, …);
       }
    }


How would you pass values between them?

Note Hooks execute on every render. That’s their whole point.

I explain this here: https://overreacted.io/why-do-hooks-rely-on-call-order/#flaw...


https://codesandbox.io/s/5vvm68k6qp. The custom effects would take functions instead of values and then `useEffect` will be executed by the component. There's certainly some issues in this implementation with regard to how to handle nested state changes, but I think that can be worked out.


I like judofyr's solution to this as well, but I think hooks that change the internal state of component are a bad idea, and breaks encapsulation in my mind.

I've used the hooks myself and I like the composability, but I can also understand the vitriol from the community. And so far my biggest complaint is that I can't use them in classes as well. I think if you treat them as a more core/composable type of object then you can use them in the same paradigm as Component classes.

My example here changes the names of the typical lifecycle events for clarity but you could use the component lifecycle names instead. Point is it should match the component lifecycle and allow the user to hook into each one separately but wrap everything up into a meaningful collection. A hook must be declaratively defined as a property in another class (I use the @annotation syntax here but I realize it's not standard yet), and that allows react to keep a consistent ordering on how/when hooks are called during lifecycle events. It also allows the hook to register itself for the lifecycle of the owner.

<code> class UseWindowWidth extends Hook {

  // normal style of using state
  state = {
    width: this.props.value
  }

  // or maybe use state hook for new style
  //@hook(UseState, this.props.value)
  //width;

  handleResize = (evt) => {
    this.width.set(window.innerWidth);
  }

  hookDidMount() {
    window.addEventListener('resize', this.handleResize);
  }

  hookWillUnmount() {
    window.removeEventListener('resize', this.handleResize)
  }

  getWidth() {
    return this.state.width;
  }

  // other public methods here would allow owner to do things
  // ? although this doesn't fit well with top-down props style
}

class MyComponent extends Component {

  // @hook registers the hook into lifecycle events and
  // passes props to UseWindowWidth hook. Also, any changes
  // to hook state should cause a forceUpdate of the component
  @hook(UseWindowWidth, { value: window.innerWidth })
  windowWidth;

  render() {
    let width = this.windowWidth.getWidth();
    return (
      <p>Window width is {width}</p>
    );
  }
} </code>

The 'props' concept might not make sense with the Hook object as I have it here, cause I can't see a nice way to declaratively update the props instead of just calling setters, but that might be ok for how these would be used.

I'm not in love with this yet, and there's some other things that would have to be ironed out (like coordination between hooks), but I think it opens some ways to allow hook use in class components as well.


Appreciate the response. I think at the moment the onus is on me to experiment with the hooks api more and hold off any judgement. Conceptually I'm fully with you, I just need some hands on experience. Plus anything that stops me making token HOC's for Redux just to interact with my global state can only be a good thing.


Here is a visual example of how hooks move all the parts of a single concern into a single block of code which, in a class component, would be littered across lifecycle methods: https://twitter.com/threepointone/status/1056594421079261185

Grouping these parts this way means they can be trivially extracted as a Custom Hook[1] (analogous to a 'mixin' or 'trait' in OOP).

By extracting this concern into a Custom Hook it can easily be reused by different components, as well as composed by other Custom Hooks. This was previously possible using Higher Order Components, Render Props and the (really old) React Mixins concept, but Hooks addresses problems with each of these previous approaches.

If you want to know more about how Hooks improves over these previous approaches, see my previous comment: https://news.ycombinator.com/item?id=19074469

[1]: https://reactjs.org/docs/hooks-custom.html


Hooks are an alternative to HOCs and RPs.

Their bad part is, they add another concept besides components.

Their good part is, they allow to convert non-display components to hooks, which removes the guessing of where the logic is stored.


It's a little harder to wrap your head around for sure.

But the benefit is tighter encapsulation of functionalities. No longer do we have to spread a feature's logic across multiple lifecycle methods that may share that space with logic from other features.




Consider applying for YC's Spring batch! Applications are open till Feb 11.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: