top | item 18302162

Introducing Hooks

501 points| sophiebits | 7 years ago |reactjs.org | reply

296 comments

order
[+] adriancooney|7 years ago|reply
My gut reaction is that Hooks isn't the greatest addition to React. One thing I've always pitched about React is the clean and extremely explicit API (with `dangerouslySetInnerHTML` being my favourite example). The hooks API is taking the dangerous road down to implicitness and magic which can only ever mean bad things in my book.

It's really not clear to me how calling the setter for these individual pieces of state triggers a render. It seems the `useState` call is implicitly linked to the calling component no matter how far down the call stack (with only a linting check keeping the safety on this footgun). I was also surprised to see the concept of reducers making their way to the API. We're being told the "classes are the biggest barrier to learning React" yet the notoriously difficult concept of reducers has its own method. Don't get me wrong, I _love_ Redux however I'm not sure I can get behind the shade for classes.

I'll have to play around with hooks before I can make a final call though because I have the utmost respect for everyone behind this project.

An interesting observation: I think this is the first piece of API that contains the word "assumes" [1].

[1] https://www.google.ie/search?q=site%3Areactjs.org%2Fdocs+%22...

[+] acemarke|7 years ago|reply
Have you looked at what `React.Component.setState()` actually does under the hood? The logic isn't implemented in `React.Component` itself - instead, it tells the core React rendering logic to queue up a re-render. The real work is all inside React's internals.

I agree that the `useState()` aspect _looks_ a bit magical, but it's ultimately doing the same thing that `setState()` does in the end. React already knows what component this is, and you're telling it to queue up a re-render for that component.

Also interestingly, it seems that `useState()` is actually a wrapper around `useReducer()`, and not vice-versa :)

[+] adriancooney|7 years ago|reply
After pouring over this API for the last 3 days and keeping up with the RFC, I think I have reversed my position. I felt I couldn't leave this comment like this because it doesn't reflect my opinion anymore. I think hooks are amazing. If the React team can nail the API down, I think hooks will be revolution in how we work in React. I still feel like it will make the barrier for entry a little harder but as an experienced React dev, they're fantastic.
[+] rectangletangle|7 years ago|reply
Agreed, the explicit nature of React is a really appealing aspect of the framework to me. Coming from Angular 1, React is remarkably "magic free," while offering a comparable level of functionality.
[+] ajcodez|7 years ago|reply
Agreed it’s implicit. I think it works by keeping track of start and end of Component rendering and counting the function calls to useState in between. You can imagine the following sequence of events.

- Enter Component A

- Use State A0 (does not exist, set default value)

- Exit Component A

Where calling setA0 sets the value at index 0 for Component instance A and queues render again.

- Enter Component A

- Use State A0 (has existing value)

- Exit Component A

[+] yaymo|7 years ago|reply
i don't trust anyone who "loves redux"
[+] sadturnip|7 years ago|reply
> 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.

[+] btown|7 years ago|reply
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.

[+] eloff|7 years ago|reply
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.

[+] sdegutis|7 years ago|reply
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.

[+] bendavis381|7 years ago|reply
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.
[+] ergothus|7 years ago|reply
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.

[+] taternuts|7 years ago|reply
> 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.

[+] paultannenbaum|7 years ago|reply
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.
[+] sophiebits|7 years ago|reply
(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.
[+] pier25|7 years ago|reply
> 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.

[+] christopherscot|7 years ago|reply
In my experience classes are one of the few boring, easily understood parts of react for almost everyone I mentor/work with.
[+] AriaMinaei|7 years ago|reply
I went from skeptical to sold in five minutes. This API is absolutely beautiful. I have quite a few components in each of my projects that do nothing other than call lifecylcle methods (they render `props.children`). Composing them is sometimes easy, and sometimes awkward. Especially when trying to read a value back from a child component.

With the hooks API, that child component can be rewritten as a simple hook, making it much easily composable.

Thing is, hooks are very simple, and some of them (except `useContext()`) are expressable with react's current API. I'm surprised that none of us thought of them earlier.

[+] vga805|7 years ago|reply
Agreed. Having used React since the beginning and having taught it, this seems to me its natural evolution. I also thought the API was beautiful. I also think it will be easier to teach, but I'm less certain about that.

While I think its a great API, I totally understand some of the hesitance expressed by others. Its more churn, etc., it seems to allow for more spaghetti code for intermediate devs perhaps. But, I don't mind the churn, to me, while its been a nuisance, it also often appears like an evolutionary process.

[+] wongarsu|7 years ago|reply
> I have quite a few components in each of my projects that do nothing other than call lifecylcle methods (they render `props.children`)

I have a few of those too, but I feel like ES7 decorators already solve this problem quite well (e.g. [1]). But I guess hook will generate better code for prepack than decorators will.

1: https://medium.com/@jihdeh/es7-decorators-in-reactjs-22f701a...

[+] emmanueloga_|7 years ago|reply
Looks like the default way of writing react components in ClojureScript (with Reagent [1]). Compare:

CLJS, Reagent:

    (def click-count (r/atom 0))
    (defn counting-component []
      [:div
       "The atom " [:code "click-count"] " has value: "
       @click-count ". "
       [:input {:type "button" :value "Click me!"
                :on-click #(swap! click-count inc)}]])
JS, React + Hooks:

    function Example() {
      const [count, setCount] = useState(0);
      return (
        <div>
          <p>You clicked {count} times</p>
          <button onClick={() => setCount(count + 1)}>
            Click me
          </button>
        </div>);}
Not sure about the name "hooks". Trying to figure out why they name them like that.

1: https://reagent-project.github.io/

[+] rpeden|7 years ago|reply
I don't think it's quite the same, is it?

In the Reagent example, you declare the atom outside of the component function, and that atom would end up shared if there were multiple instances of the counting component.

In the react example, each instance of the component would have its own separate state.

Reagent will let you put the atom inside the component, too, so you can do something very similar to the React example. Just wanted to point out that the two examples you've got now do different things.

[+] lilactown|7 years ago|reply
Yep. It looks like React is eating Reagent's lunch and I'm super excited about it!
[+] anonytrary|7 years ago|reply
Unpopular opinion: There are too many cooks. I used React two years ago because the API was tiny, simple and elegant. They are adding new API features left and right, and the framework feels very uncoordinated now. The surface area is way bigger than it needs to be. This is my cue to finally explore other frameworks.
[+] hliyan|7 years ago|reply
"You might be curious how React knows which component useState corresponds to since we’re not passing anything like this back to React. We’ll answer this question and many others in the FAQ section."

While I appreciate the functional usage of state, this type of magical behavior worries me a bit. Wasn't more straightforward semantics possible (even if the syntax wasn't similarly straightforward)?

[+] Fellshard|7 years ago|reply
This is where I'm falling on it. What started as initial excitement over a lens-like addition started to feel more like black magic once I realized that it relies _a lot_ on implicit ordering just to preserve a seemingly simple usage pattern.

While it may _look_ nice to be able to just call `useEffect` and have it infer the rest, it just ends up masking the flow of a hidden parameter into that component, and convolutes the user's understanding of what React is actually doing.

[+] joelg236|7 years ago|reply
In the keynote, Dan talked about how this works. It depends on the order that useState is called, which makes conditional branches a no-no (there's a linter for it).
[+] spankalee|7 years ago|reply
It'd be really great for VDOM, builder APIs, and any immediate mode -> retained mode mapping, to be able to get the _callsite_ of a function invocation. Then they could associate state with the callsite unambiguously without resorting to tracking invocations on a stack and disallowing control flow.

Tagged template literals already give us something of a callsite, since the template strings object is cached per callsite. You could do something ugly and use that to associate state:

  const state = useState``();
[+] kiliancs|7 years ago|reply
> React assumes that if you call useState many times, you do it in the same order during every render.

Note they also say:

> We provide a linter plugin to enforce these rules automatically.

Makes me feel a little better.

[+] eric_b|7 years ago|reply
There are a lot of gotchas with these. Don't call in conditionals, branches, loops. Can only be called from function components. Order of invocation matters (yeck!)

If the goal is helping developers "fall in to the pit of success" I think classes are a much better option than this.

[+] pg_bot|7 years ago|reply
I know I may be in the minority here, but I can't say that I'm a fan of this feature. I can't envision a scenario where this would be my preferred solution to any of the problems that they detail. Templates should remain stateless, as they are far easier to reason about that way. My gut reaction is that this is a giant step backward.
[+] pavlov|7 years ago|reply
Am I getting this right: it’s an implicit global stack for the render loop? Sounds like a combination of the worst design features of OpenGL and Forth, to be honest...

OTOH, React has been pretty good about taking discredited design ideas — like Adobe Flex’s style of mixing XML declarations into ECMAScript code — and injecting them with new vigor. So maybe this one too is better than the initial impression.

[+] ggregoire|7 years ago|reply
While I agree that class components has always felt like a workaround to bypass the limitations of function components, and that it's obviously annoying to rewrite a function component to a class component just to add a state or a lifecycle method, the following explanation sounds a bit silly to me:

> In our observation, classes are the biggest barrier to learning React. 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. Without unstable syntax proposals, the code is very verbose. People can understand props, state, and top-down data flow perfectly well but still struggle with classes. The distinction between function and class components in React and when to use each one leads to disagreements even between experienced React developers.

A framework shouldn't be designed around the fact that people don't know the language they are using, right?

Anyway, those hooks look like a good way to solve the issues listed above. Can't wait to try it out!

[+] schneidmaster|7 years ago|reply
In the live talk, Sophie also discussed how Javascript classes are difficult for machines: minifiers aren't able to shorten the names of methods (because it's apparently hard to work out all the ways that the method could be invoked), and they cause stability problems with hot code reloading. So there are benefits beyond ease of use for humans.

(Also, I'm pretty fluent in Javascript and I still forget to bind event handlers all the time, which suggests to me that it's a counterintuitive pattern even if I know "how it works" on a technical level. And you either have to put a bunch of "this.handler = this.handler.bind(this)" in the constructor, or rely on class properties which are an unstable syntax feature, both of which are suboptimal.)

[+] coldtea|7 years ago|reply
>A framework shouldn't be designed around the fact that people don't know the language they are using, right?

No, but it could be designed around the fact that some of the language features are confusing or crap.

[+] vinceguidry|7 years ago|reply
At the end of the day, the framework that allows people to get more done with less thought is going to win the most mindshare.
[+] paultannenbaum|7 years ago|reply
Another great option to bypassing the limitations of functional components is using a higher order component library like recompose[0]. Does have somewhat of a higher learning curve, but I personally consider it a must have tool on all my projects.

[0]: https://github.com/acdlite/recompose

[+] beatgammit|7 years ago|reply
To be fair, JavaScript has been a moving target and things like classes are quite new. Then again, it doesn't make sense to avoid features just because they're new.

I think the point is that they want to provide two ways to do simple things: classes and functions. Functions have better performance, classes are more flexible.

[+] nathan_f77|7 years ago|reply
This is really interesting. I'm a big fan of any abstractions that reduce the amount of code I have to write. Just a personal preference. Other programmers like to avoid "magic", but I love it.

`useEffect` feels much cleaner than the component lifecycle methods, and I could add some abstractions on top of those. e.g. a higher-order function that adds/removes a window event listener, and you can just pass the event and your callback.

Maybe `useState` could take a second argument that gives the state a "key", so that it can be called in any order. I can imagine breaking up the `useState` calls into different functions, and then skipping one of the functions if something changes. So it might be handy if there was an escape hatch where you could label each call with an integer or a string.

EDIT: Wow, it would be awesome to use this pattern for my Redux reducers. Instead of having to connect up all the actions in my container and then prop-drilling all of the dispatching functions down into my components, it would be great if I could just import the action creator directly and use it anywhere I want. Maybe I could add some similar hooking magic to find the store from the Provider and call dispatch with the action, instead of manually wiring everything up. Although actions are usually called after the render is finished, in onClick handlers, etc.

Or maybe I could do this with Context. I'm just really tired of prop-drilling all these action dispatch functions down to my components. There must be a better way to automate all of the boilerplate with actions and reducers, even if it's a Babel plugin.

[+] acemarke|7 years ago|reply
Hi, I'm a Redux maintainer. If you've got some suggestions on how we can improve the API and usage, please file an issue!

I'm currently looking at revamping our WIP React-Redux v6 branches to use hooks internally. Looks really promising so far, but I'll have to keep playing with it and see how it turns out.

Our goal at the moment is to publish React-Redux v6 that is basically API-compatible with v5, and then open things up to discussion about potential alternative API approaches for a future v7.

[+] WorldMaker|7 years ago|reply
Why not support Hooks in classes as well? The motivation is correct in that Hooks can help separate intertwined concerns that are currently mingled in the lifecycle calls. There seems a good usefulness in allowing the same cross-cutting inside classes as well as functions.

It would also presumably help avoid a "fork-the-world" situation where projects eventually find themselves having to maintain both HOCs and Custom Hooks side-by-side permanently as the userbase forks camps between the two. Potentially causing a maintenance hazard.

Certainly you can use one from the other one in component nesting, but particularly in cases where maybe a class has an inconveniently complex structure today, being able to refactor say all the state handling code to use hooks without having to refactor all of the side-effect handling code at the same time could be crucial for some projects in adopting Hooks.

[+] eknkc|7 years ago|reply
I’d say the useState hook should receive a name parameter first and not depend on the call order. That way one can do branching, loops etc. This way it feels like too much magic.
[+] lacker|7 years ago|reply
This looks like a nice but not enormous improvement to React.

First, I really like how these hooks are optional. You can still use the current class-based mechanisms wherever you want. Existing code will keep working the exact same way.

For the specific hooks they provide:

`useState` seems like a slight improvement over using `this.state`, at least for simpler use cases. You save about 5 lines of object-oriented setup, which is nice. You also avoid scattering your code all over the place - you can now initialize your state variables in the same place you are using them. The only downside I can see is that if your initial state is constructed in a complicated way, `useState` doesn't seem to make it easy to do that only once. When you state is just initialized as 0 or null it's fine.

`useEffect` looks a little confusing because it is passing a bunch of anonymous functions around. It seems like a slight improvement if you have a lot of side effects that need cleanup, because you can put the code in one place instead of several different functions.

Hard to say how useful custom hooks will be. It might be easier now to put functionality that uses state in third-party libraries. For example, this `useDragGesture` hook: https://twitter.com/grabbou/status/1055521332031512576

The last thing that seems clear is that React embracing more of a "functional programming" direction. I think some people will like this and some won't. The nice thing is that you don't have to go all one way or the other - this change seems like it is really about making all of React's features available if you do want a functional programming style, since all of these features continue to exist in an object-oriented style.

[+] _uhtu|7 years ago|reply
I have some troubles with the state hook. It may look nice to a novice developer, but when I look at it I just see confusing magic. I read what's happening and I just say wait, there's no closure or class here, how this state is stored is completely hidden from me.

I can see uses for the other types of hooks, but the state hook seems like it would be much better served with an HOC like Redux's connectToStore, which would be concise and non-magical.

[+] manigandham|7 years ago|reply
This looks like unnecessary magic. Classes are a core programming concept and this seems to be trying to get JS devs who didn't deal with them before to try and learn something else, while being completely non-transferable to any other language.

Making the built-in state management functions simpler would be better, as well as using newer JS constructs like decorators (aka attributes) to wire things up declaratively. MobX has been leading the way here for awhile with a very smooth dev UX that's even better than this Hooks proposal.

[+] philosopherlawr|7 years ago|reply
This document give me tinglings. Something about how the hooks just click with me and I can see how the web changes with WebAssembly and multiple compile targets (native, desktop, etc.), this will make our lives easier.
[+] sergiotapia|7 years ago|reply
So adding class lifecycle events to functional components? Why not just use a class?

The beauty of the functional components is that immediately you know there's ZERO state in here. It's just rendering markup based on input.

Now there's more to look out for given this `hook` thing. I don't like it.

[+] pcmaffey|7 years ago|reply
I think this is a really positive solution to creating a streamlined functional api.

However, in its simplicity (and hidden magic), I fear that it won't encourage junior devs to understand what's actually going on, and thus may lead to bad code (eg monster components with lots of side-effects running on render).

In a way, this reminds me of MeteorJS, which was awesome for new devs getting up to speed with a powerful JS environment under the hood. However, by internalizing much of that power, it became too closed off, too difficult to build and compose new patterns around. And is now mostly irrelevant. Not saying that's React's future (I'm all-in on React and excited for this), just a concern / thought.