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].
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 :)
The 'this' in 'this.setState()' tells react which component queued the setState. With the new API that information is collected by react through.. ehm.. some kind of magic.
I understand why the react developers chose not to make the component an explicit argument in the new API, as it would open doors to misuse. But magic never seems to work out in the long run, IME.
I'd agree it's "magic" in the sense that it's not entirely visible to the end user. But, the key is that React is _already_ tracking _which_ component it renders as it traverses through the component tree. It's just now also keeping track of some additional data as it goes through the process of rendering that component. So in that sense, it's not any more "magical" than any of the existing render algorithm.
The problem is that the hooks seem to be much more restricting than setState. You can `if (condition) this.setState({x: 1})` but you can not with hooks because React keeps track and doesn't allow you to break out of a very narrow usage.
Look at the rules from the docs:
> Only call Hooks at the top level. Don’t call Hooks inside loops, conditions, or nested functions.
> Only call Hooks from React function components. Don’t call Hooks from regular JavaScript functions.
I think this will lead to junior developers to not understand the React framework but to just accept "the way to do things" and do what the docs say, without ever questioning anything.
Yes, I think you’re right, there are different restrictions on when useState and setState, though it is worrying seeing confusion straight away about it.
I wonder about a case where there is an expandable panel in a UI, and when expanded it should fetch then display data from a web service. Easy enough to do with this API by splitting the content of the panel out into a second component, but it is going to be very tempting to wrap that useEffect fetch call in an `if (expanded)` condition.
On the other hand, there are lots of positives about this design too, the correctly written code does look very elegant and I can see it solving real problems.
As I understood it you should not opt-in to use state conditionally in your component but you can still conditionally set the state. That means you should not conditionally call `useState` but it is fine to conditionally call the `setState` returned by the `useState`. That is not different to how you would use state in a class based component where you also wouldn't attach a state `this.state = {...}` somewhere in the middle of its lifetime. The component has state from the beginning of its lifetime or it hasn't state at all. There is not such concept as "Now that you are expanded you will transform into a stateful component".
What is worrying about that useState/setState example? The person just didn't read the page properly, it's not an indictment of the API. Maybe give it a month or two before the claims about confusion, this was just someone skimming the page and missing some details.
I think it's best to treat the various hooks the same as method definitions: you wouldn't conditionally define componentDidUpdate or componentWillUnmount in a class component. Instead, you would conditionally do something within those (always-present) functions. The same applies for these hooks.
I'm unsure what's ironic about it. Hooks feel significantly easier to me than classes in a bunch of dimensions.
Classes come with a whole lot of syntax & concepts like constructors, instance vs class properties, autobinding, that are otherwise not used at all in React. They present barriers to optimizing code with compilers, and property names don't get minified.
Hooks are Just Functions though. They don't even have to deal with the most confusing aspect of functions in JS: "this"/calling context. You could easily consume them in ES3 by just not using array destructures and function keywords and it would barely get less readable.
Contrast this with classes, where new devs are often pushed to learn experimental and often-changing syntax just to avoid the awkwardness of working with the current specification for classes in JS. The following isn't allowed per the spec or any stage 4 proposal, but it's all over intro tutorials so that people don't have to manually bind methods in the constructor.
> I'd agree it's "magic" in the sense that it's not entirely visible to the end user.
I didn't dive into hooks to completely yet to have a proper opinion on it, but the primary disadvantage of "magic" is that the end user can not deduce what's going on just by looking at the code. If magic is therefore "only" restricted to the end user, that's not much of a restriction...
There's already so much magic going on in React's code base. For Fiber, they put the stack in the heap so they could go around the JavaScript call stack and schedule work as they see fit
Oh absolutely - by now I have read through the docs, and as I mentioned at [1] as well, I'm even expecting hooks to do away with more magic than it introduces.
That said, my main point was that "it's only magic for end users" is not really a valid excuse :)
Agreed, it's not really a valid excuse. Hopefully a scheduler re-write comes along soon to alleviate those concerns. I absolutely share them; I can't read any of that code anymore
I’m guessing that React is keeping track of the current component instance that is executing when the hook is called, instead of JavaScript keeping track of what ‘this’ is bound to when ‘this.setState’ is called. One could say that both systems are “magic,” but it looks like the React team considers the behavior of ‘this’ in JavaScript to be a source of confusion for React developers.
You can see the current implementation in react-dom@16.7.0-alpha.0 - see https://unpkg.com/react-dom@16.7.0-alpha.0/cjs/react-dom.dev... for the actual code. Do a search for some of the effect names, like `useLayoutEffect()`, to see the implementation details.
React already knows which specific component it's rendering, regardless of whether it's a class component instance, or a specific function component. Looks like what it's ultimately doing is just tracking some additional metadata added to React's internal bookkeeping - search for instances of things like `workInProgressHook.memoizedState`.
And as someone who spends a large portion of my time answering questions from new React devs, I can confirm that the behavior of `this` in classes is an _endless_ source of confusion. Mitigating that is going to be a big improvement long-term.
I’ve seen this discussion pop up before in other frameworks I have used as they evolved over time. Interestingly enough my favorite framework went from being very magic to being less magic and more explicit and verbose. I liked it better when it was more magic.
Guess I’m in the silent group who isn’t bothered by a bit of magic, and actually likes it. Sometimes I don’t care too much what’s going on under the hood so long as I understand what to expect to happen. If it’s a good framework, magic or not, the documentation will do a good job of detailing how to use such magical features.
Magic is great when it works. The problem is when it doesn't do what you are expecting, it can be much harder to troubleshoot. (Did I do something wrong? Am I hitting a bug?)
Magic hides internal details, hence users are less familiar with them, hence they struggle more to diagnose when the magic fails to happen.
The magic isn't what React.Component itself does, really it's the use of global variables (or close enough), because there is no other way in js to know inside useState what component called it.
The only way I can see this implemented is that when react calls your component function, it then keeps it a note of it in somekind of globally shared variable and then when useState is called it access that same shared variable to get the state.
It's also dependent on call order - that's a sure sign of magic work.
The "memory cells" the FAQ mentions sounds awfully like a map structure where React uses some internal identifier generated on first render to map any state handlers. The only "magic" bit is that it relies on the order of the `use` calls to be the same every time.
Really either react notifies or registers the component to the module to exports useState, or more likely useState goes to some react internal api and retrieves the currently running component.
It's as much a global variable as the singleton pattern.
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.
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.
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.
> 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.
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.
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
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!
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.
> 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.
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.
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.
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].
I think you need to use sagas for either all your logic, or none of it. Having it half/half just means you’re always searching for where something is located.
That said, thanks for the recommendations! I’ll check them out.
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).
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.
> 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 can’t say I’ve had that much trouble bringing in third party libraries, nor have I had a need to change how the framework works internally. I think this is the same as with most frameworks, you tend to just use them rather than doing stuff under the hood. Can you give an example?
At this point not really... it's been over a year since I've had to really worry about it. It was better using angular-redux or ngrx, but still was a weird experience. When you have eventing issues with RXJS, or handling integration with other libraries, it's definitely not as broad or mature as the React space is. Anything from chartjs to color pickers, I'm trying to recall the last big issue I had in an angular app. In the end, I just remember pain all along the way more than most specific examples.
Oh yeah... drag-drop integration... that was the single biggest pain. None of the component libraries worked well, and doing it manually was harder than I've experienced before, including just straight JS, and definitely worse as a whole than using say jQuery-UI.
If it were a year or more ago, I could cite better examples of pain points. 2+ is better than 1.x, but still horrible. I'd rather see people adopt Vue over Angular. I still prefer a react-alike over any of them.
Funny you should mention drag drop, it’s now built in as of version 7, just released a few days ago. Look up the CDK for more details.
Whenever I’ve needed drag and drop functionality in the past I’ve found it in existing UI components. A fairly comprehensive one is primeng. I’ve also added an angular colour picker and highcharts without very much trouble.
Often when I hear people complain about angular, I wonder if they just don’t understand it very well and are trying to do things the wrong way. Angular is opinionated, but I don’t find it terribly difficult or painful, though at this point I do have rather a lot of experience with it and find it’s difficult to put myself back in the shoes of someone with less.
I really do enjoy the framework and how comprehensive it is. I like having a framework that gives me so much out of the box without having to go to a somewhat unstable ecosystem of related projects. Frankly I think Angular’s bad name is due in part to it simply being fashionable to put Angular down, and partially because it’s critics never really learned how to use it.
Also, something about jsx just rubs me up the wrong way.
I feel the opposite... I feel like React, by comparison, and JSX are much like the huge lego sets you used to be able to get generically... being able to plug in whatever I want. Angular feels like being stuck with a Duplo version of MindWorks.
As to JSX, it's something close to what I wanted a long time ago... when E4X was added to Mozilla's browsers, along with support for it with Flash/ActionScript3 and XML literal support in VB.Net (not that I love that language) ... interactivity to the UI was SO much simpler. The shame is it was never really supported in other browsers, which left it dead on the vine.
When JSX came out, it's REALLY close to what I already wanted, and in most cases better. In the end, I can compose a React/JSX component starting with a static render function in a file by itself... I can grow it and turn it into a class or wrap it in higher order components. Compose with extensions like react-redux, material-ui, react-jss with ease. Every single time I write an Angular component, I'm looking up configuration arguments for @directives. It's just painfully restrictive by comparison.
That's off the top of my head... that's just the code you have to write... Now, do the same thing in Angular off the top of your head, I'm willing to bet it's a LOT more verbose, ugly and hard to remember/modify what you need.
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.
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 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.
I know, but lately during frontend interviews I was surprised how many times I met with "I'm a developer I can do anything" type interviewers - some of them were dotnet, others were java devs and they preferred typescript, because 1. javascript is a terrible language 2. with typescript they feel right at home 3. they can use a "proper ide" (please, don't ask, I already had an argument and a rejection when I tried to ask about webstorm)
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).
The article says bind as a negative point - I perefer the new non-bound class method syntax:
```
private handleClick = (event) => {}
```
By "mental lazyness" I mean that people (as in people I have met with) piss on javascript and praise typescript and in the process they never bother to learn about javascript.
I do like vue, react, angular of course, but I don't think that you become a frontend developer from one day to the next and keep saying that typescript is _exactly_ like java (or dotnet).
> 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.
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.
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.
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.
> 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.
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.
Now the atom is defined inside the component function, and is captured by lexical scope in the returned function. Any timer-components on page will have their own `seconds-elapsed` state.
This was such a common pattern that reagent created a macro for it. But I like the macro-less code, in that it becomes very clear what is happening. A good macro reduces complexity by making code concisely simple. A bad macro hides complexity by making code easy to write.
Frameworks like React succeed when they are built like a good macro.
In this case, React is both hiding complexity (component lifecycles) while adding unnecessary complexity (a lInter that will inevitably fail). It’s odd to see React make this move, but not surprising considering that it now has a larger use-base then ever before.
This is a bizarre comment to me. React is already used _way_ more than Reagent. Even Reagent itself uses React internally. What "lunch" is there to eat? People who enjoy ClojureScript will still use Reagent/Re-Frame because it's not JavaScript.
I think it refers to adopting a pattern from Reagent.
Throughout its lifetime, React does seem to have had a mutually beneficial relationship with the ClojureScript ecosystem, with select ideas from Reagent, Om and others being adopted in core React, or as JS libraries around it.
In the small world of the CLJS ecosystem, we use some very elaborate wrappers around React (for good and bad reasons). I'm excited to have many of the features we have had to implement and maintain in user land be moved into React proper, so that we can remove swathes of code that were filling in gaps in the React framework.
In general, I am much more confident in the React team supporting these concerns - simply because there's WAYYY more time, money and mindshare involved - than the CLJS ecosystem shouldering the burden.
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.
I tend to agree, but you don't have to move at the same speed as the framework. If you have a version that is working well for your needs, stick with it for a year. Then decide when and where to upgrade. You can let the dust settle on new features, let other people learn the harsh lessons and come up with best practices, and then you follow in their footsteps, stably upgrading your codebase to exactly the point that makes sense.
Or, if you prefer a business cliche, prefer leading edge over bleeding edge.
"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)?
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.
I think people are getting too hung up on the "implicit ordering" thing. The only thing that really matters about the ordering of hooks is that the call pattern remains stable between render passes. This code:
Component lifecycle methods are easy to grasp conceptually. At different stages of rendering the component, React will call one of these methods. Easy.
With `useEffect`, I have no idea what is actually happening here, and I've tried re-reading it a few times.
They're easy to grasp conceptually, but they're a pain to use in practice specifically because they force you to split up code which is conceptually related. I've had to deal with some truly monstrous class components in the past which would have been a hell of a lot easier to deal with if we had this.
As for `useEffect`: think about the kind of logic you'd put in `componentDidMount`, `componentDidUpdate`, and `componentWillUnmount`. In CDM, we set up side effects, in CDU, we update those side effects if needed (based on the props, state, and context) and in CWU, we clean up those side effects. If we squint a little, though, we're really only doing two actual operations in those three steps:
didMount: perform side effect A based on props/state/context
didUpdate: clean up side effect A, then perform side effect B based on props/state/context
didUpdate (again): clean up side effect B, then perform side effect C based on props/state/context
willUnmount: clean up side effect C
So the idea here is:
const [name, setName] = useState("world");
function modifyTitle() {
document.title = "Hello, " + name + "!";
return restoreTitle;
}
function restoreTitle() {
document.title = "Untitled";
}
useEffect(modifyTitle);
When this component is rendered, it'll run `modifyTitle` on the first pass, and store its return value (`restoreTitle`) for later. When it's rendered the _next_ time, it'll call `restoreTitle` first and then `modifyTitle` again... which itself returns a new "cleanup function".
So we basically have the same sequence of calls as this is mounted/updated/unmounted:
perform side effect A
cleanup side effect A, then perform side effect B
cleanup side effect B, then perform side effect C
cleanup side effect C
The only difference is we never told React _when_ to perform these steps, just _that it should_ perform them at the appropriate time.
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).
Since JS is single threaded, they are basically keeping track of which component was last called for render - that way they know useState() calls attached to which components. If the order changes, then they can't keep track of the different objects tracked by useState. So treat it like you would class state - all fields are always there, no matter which functions or conditional branches you take - hence - do your initialization above all your conditionals and loops.
Edit: And yes, slight concern of course, it's the got some rough semantic equivalents to a thread-local in Java, albeit in Java the danger exists downstream, this danger is only exhibited during initialization it seems.
IIUC, the order isn't used to figure out which React component is being rendered, but which of potentially several hook invocations within that single component is currently being invoked. If every unique hook could only be (directly or indirectly) called a single time per component, then there wouldn't need to be this ordering restriction. (Or more realistically, if each hook call also took an ID that is unique per-component.)
Just looking at the code I came to the conclusion it had to work that way, I don't like it, way too easy to break and some people are going to be very confused when things goes wrong. It's a uncommon/unfamiliar coding constraint. If you have to declare them all in the same order every time, you might as well force a single state (like the setState API) and/or keyed state values and have a safe API.
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:
I doubt the linter will enforce the same-ordering, that would require state between runs. I think the linter just ensures hooks are run at the top level in the functional component.
And it wouldn't need to. You're free to move around calls to useState() to your heart's content in your code, you just need to call them in the same order during the lifecycle of your component.
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.
There are a lot of gotchas with the object-oriented way of doing these things too. Don't set `this.state` directly, don't forget to keep your side effect logic in `componentWillMount` parallel to your cleanup logic in `componentWillUnmount`, et cetera.
Yep, no doubt. The problem is they aren't getting rid of old foot guns, just adding new ones. And the new way with hooks isn't obviously superior to the old way (debatable to be sure, but with all the constraints hooks are not a broadly applicable solution to many problems). So now you've got two separate but equally powerful guns pointed at your feet.
yep I see nothing wrong with classes, infact I think I'm going to go back to an older build of React. All this suspense, the messy fiber rewrite and all is just screwing up library.
LONG LIVE class based component, there was nothing wrong with them. Don't fix it if ain't broke.
If an older version of react is the goal, maybe infernoJS would be the ticket. It's way more performant than most versions of React while being mostly API compatible (not sure about the most recent react changes).
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.
Are you referring to hooks in general, or the specific useState hook they showed?
I love the idea of hooks - the more I can isolate my logic, the better. The useState example I'm pretty ambivalent about, for the exact reason you state.
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.
Suspense is also based around throwing promises. React isn't afraid to go against the norm. So far they've been pretty successful, willing to give the benefit of the doubt.
Personally, I loved Flex’s mxml/actionscript combo. Its main (fatal) flaw was targeting the flash player. At the time, moving to html/js felt like a big step backwards on developer experience.
Yeah, MXML (and in fact most of Flex) had a lot of good about it. “Discredited” was too strong a choice of word in my post — I meant something closer to “unfashionable”.
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!
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.)
It may have been on purpose (came off as a sincere mistake), but Dan Abramov forgets to bind them in this talk while giving an example. It's a very common problem, even for the authors of React itself :)
It did come off as a sincere mistake, but remember, they had a dry run of the talks a day or so before. So could have easily been staged. But was hilarious anyway.
Doesn’t that example also make it impossible to minify the names of anything? Perhaps other than JavaScript modules, since AFAIK the popular module builders like Webpack do compile-time module importing (I believe that even the dynamic import function is recognized at compile time and doesn’t support throwing in a prompt).
No, that issue is unique to class/object property names. You can't dynamically reference a variable in a scope by name supplied by a variable in the same way (except by using `eval`, which does in fact cause minifiers and javascript engines to disable a bunch of optimizations in the scope it's used).
Of course it's unintuitive. I see dev veterans make the mistake all the time.
JS was so poorly designed. An implicit (not passed as an arg), dynamic this parameter. You couldn't be more cheeky than that really.
something like console.log.bind(console.log) is beyond silly. It's what we're stuck with but I will minimize the occurences of that kind of crap any single time I can.
That is unfortunately not true. The framework with the best marketing will win.
That's why frameworks like angular and react are so popular. Both are designed to work well in large teams that work on huge codebases. Most of us probably would came along with much much simpler solutions.
So are you telling me Facebook has a serious marketing budget for React? If so, that means tech has gotten way way crazier than I could ever have imagined.
There's a complexity angle that needs to be appreciated. I love Rails, I think it's the best solution to the problem it tries to solve for the people it wants to solve that problem for. But it's been called overly-complex and magical. But less-complex, less-magical frameworks just don't solve the problem as well. Just try building something big with Sinatra and you'll see what I mean.
I'm not terribly familiar with Angular, but React, when you remove it from its ecosystem and treat it as just a library-framework, does a great job at facilitating the building of user interfaces. The abstractions and underlying paradigm are somewhat hard to grasp, but once grasped, a competent architect can lay down patterns that juniors can implement.
A lot can get done in a small amount of time with React. The people that managed to do this in the beginning are the ones who christened React the Son of UI Development. People listened, and now we have the Church of React, with the Redux Reformation now upon us. Facebook had nothing to do with it, that was all us.
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.
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.
>I think the point is that they want to provide two ways to do simple things: classes and functions.
At the risk of seeming like an old man complaining about new things, what problem would "classes" solve in javascript that the existing object syntax, which allows for inner functions and variables, doesn't?
Prototypal inheritance, and scope controls more refined/easier to work with than IIFEs. In the end the class notation is "simply" syntax sugar giving the object syntax the benefit of Function.prototype and IIFE closures, without having to resort to layers of either/both.
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.
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.
Hi, thanks for your reply! I've had a look at some of the higher-level abstractions for Redux [1], but none of them really appealed to me. I'm just looking for something magical and opinionated so that I can write less code, so I might even try to write my own abstraction. I want to see if it's possible to automatically generate actions and action creators just based on the reducer. Maybe a special way of writing reducers, or maybe leverage the type system and write a babel plugin.
There's a lot of existing libraries out there that will indeed generate action creators, reducers, etc (see my Redux addons list [0] for examples).
One of the most interesting ones I've seen is Eric Elliott's `autodux` project [1], which does some clever bits of handling inside a `createSlice()` function.
Earlier this year, I put together a new project called `redux-starter-kit`] [2]. Its goal is to simplify several common use cases with Redux usage, including store setup, reducer definitions and immutable update logic, and default behavior out of the box. While we don't plan to add these things to the Redux core itself, `redux-starter-kit` is an official Redux-branded library, and we're going to start encouraging that people use it in most situations.
We just added a `createSlice` function to `redux-starter-kit`, based on the `autodux` approach. I'd encourage you to try it out, as it does _exactly_ what you're asking for. I'd also really appreciate some feedback on how well it works, and any additional ideas for things the starter kit should include!
autodux looks amazing, thanks for the link! I hadn't seen that before, and that's exactly what I'm looking for. Looks like there will be a bit of work to add support for Flow and Immutable.js, but I might look into that.
Sure. Also, just as an FYI, because a large number of people aren't aware this is possible: you can pass an object full of action creators to `connect` instead of a `mapDispatch` function. This simplifies the use of `connect`, like:
Whoa, I just found this redux-auto [1] project, which is a really interesting approach. You just apply it as a middleware and then you can import and call actions from anywhere. The API is a bit ugly but I like the general idea.
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.
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.
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.
I can see the benefit being enormous if you use typescript. Currently it is often a struggle to come up with the magic combination of generic parameters and HoC ordering to make things work, which might still fail if your HoC has an incomplete typing specification. Using a couple of very simple easily typed functions makes things much easier to reason about and to specify correctly.
This was one of the first things I thought about when I read the docs. This is primarily meant to replace render props and HOCs, which solve a legitimate problem but are a royal PITA to use, especially with Typescript.
This is going to make it about a million times easier to use Typescript with React.
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.
I agree. Something about it just doesn't feel right to me. Like state is stored in a global singleton somewhere.
Any of the examples touch on testing components using hooks? Any chance using the same component across multiple tests would result in state collisions? Especially when testing something asynchronous?
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.
While I don't disagree with this, classes in their current incarnation were shoehorned onto Javascript's prototype-based object inheritance mechanism relatively recently. Javascript classes have enough idiosyncratic behavior that half of what you learn is essentially one-off, inapplicable elsewhere.
As mentioned in the "motivation" section[1], binding to the `this` pointer of the surrounding class efficiently in every context is not a simple proposition. The naive way to do it introduces a bug (`this` ends up pointing at the component passed the callback instead of the class) or creates unnecessary closure instantiation on every rendering frame.
A demonstration of Hooks is live right now at ReactConf and looks very cool: https://www.youtube.com/watch?v=kz3nVya45uQ (go back about 1.5 hrs into the past to see the start of the demo).
If you like live coding demos, he also gave a talk at JSConf Iceland where he showed off some future React features, such as asynchronous rendering: https://www.youtube.com/watch?v=nLF0n9SACd4
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.
This is an excellent point, not sure why you've been voted down. So now functional components can't be guaranteed to maintain their best property? Weird.
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.
This is interesting. I'll have to spend some time mulling over this before the benefits sink in. It seems like a much more confusing and less composable API than recompose, which is how I add state, lifecycle, and other React class features to functional components. I've been completely avoiding React classes for a while now and using stateless components with recompose for over a year now, and I find it to be a wonderful architecture for component testability and reusability.
Still, I'll be looking into the hooks pattern and determining how it will work for my projects and especially how easy it will be to port my large number of reusable HOCs from recompose to React hooks.
Recompose is not really compatible with TypeScript (no clue about Flow). Render props are a good alternative but the nesting made composing a bit cumbersome. Hooks are easy to type and compose quite fine it seems.
I don't know much about TypeScript, but is any React HOC function composition compatible with it? It seems like it would be difficult for TypeScript to typecheck a compose function where you can do compose(hoc1, hoc2, hoc3), where each HOC renders a child component with some props added.
You can write HoC with working typings [1]. As far as I know the compose function is harder to type because of the varargs it uses (although I heard recent TS versions can actually handle that).
What 'tree' are you referring to? Higher order components are just functional components that accept a component as a argument and return a new component. There aren't any additional nodes added to your VDOM unless you specifically make a HOC that does that.
Then yeah you are incorrect. You are not adding any additional components to the tree. Higher order components are just a design pattern. How you implement that pattern will decide on the component tree, but there is no additional "wrapping" penalty for using them.
I absolutely fell in love with React when I switched over from Angular many years ago. Loved the simplicity of the API and how you had to be explicit about everything. Over the years, I've seen React start adding a lot of "optional" APIs which increase the surface area anyway. This made me explore other options and now that I've started doing some Elm, I feel it's so much easier to reason with.
I guess this is the natural progression of frameworks. What was once simple will get complex until the next thing comes around.
Well, okay. Preact exists. Even mithril.js isn't completely dead.
Sad, though. A framework can afford a lot of technical debt, but adding more conceptual debt, like React was doing recently, seems to me like a road to oblivion^W legacy status. Features that lack clarity and come bundled with footguns tax developer's brain resources, and increase the rate of errors. These are some of the most expensive resources in IT, and fighting errors lengthens time to market.
v2.0 is coming. Breaking changes are minor stuff. Migration of my very large app took me about an hour. The API of mithril is pretty much settled, which IMO is a good thing. We even plan to remove stuff for next major. Simplicity is key.
Yes, if we decide not to include this we will definitely back it out. However we’re excited and hoping that we can agree on something everyone will love.
We could use a separate branch but in practice we prefer to use feature flags for development; we find it easier to manage. Once we reach a definitive conclusion on whether to include it, we’ll remove the feature flag.
Oh great. Another React feature that relies on creating anonymous functions every time your component renders.
I presume this is now adequately fast in the various JITs in use, because I seem to remember this being one of the major bottlenecks React developers were instructed to avoid doing at all costs.
Nah, this has nothing to do with that. If you pass an anonymous fn to a pure-component its shouldComponentUpdate part flags it for re-rendering since the property is new every time, which can be a problem depending on how large its payload is.
Creating a function costs you a few clock ticks, there is no bottleneck, rendering a component and its entire sub tree for no reason on the other hand can be critical.
I don't want useState to magically determine which state to use via call ordering. I want to pass a unique ID string to useState and have that determine which state I want.
Order-based management could then be opt-in, and based on a non-magical ID-based system.
That's pretty much what useState does. (or maybe you were already saying you liked that -- sorry, if that's what you meant) The only difference being that the "getter" has already been "executed" in a way: useState returns a value and a setter. Changing the value won't get tracked, and a call to the setter will overwrite the old value and re-render.
Yeah -- I'm saying it's dubious that there's magic here "doing the setState queue for you" -- notice the function call isn't bound and there's no passing of "this", so it's gotta' got some magic under the hood with some global context.
I do, however, like the concept for the react style of render -- being able to grab a getter/setter from that is awesome. They can remove the magic by simply doing:
No sir, I don't like it :/ I don't see a simple API, I see a completely magic black box that is going to chew up my fingers if I decide to put my hand in it.
Don't fight the language! JS is a still an evolving language, class properties are at Stage 3 draft, and have effectively been usable for years already. I feel the verbosity argument against classes is complete bunk.
When you write "TLDR: There are no plans to remove classes from React... Crucially, Hooks work side-by-side with existing code so you can adopt them gradually... Finally, there is no rush to migrate to Hooks." it doesn't give me the warm and fuzzies, quite the opposite. I can feel the momentum is going to shift to the new hotness and a completely different way of doing things and I'm not sure I want to go where you're heading. Until the next shiny thing.
I wish React would stop adding to the core library. I strongly feel most of the innovation should be happening in libraries surrounding it, even if they're first party libraries, with the core being much slower to change. A good reason to change the core would be taking advantage of new standardized JS features in the future, or (minimally breaking) changes to support big ideas like async rendering. Changing the fundamental way you write React code because "classes are confusing" is just a terrible reason!
Someone much smarter than me said something like "good magic decomposes into sane primitives". Jury's still out on whether Hooks and "useState" qualify.
I wish they'd work on global event based system to pass state, like redux but built in and steam line it for all use cases like promises. React state propagation sucks.. what Vue is doing with events is basically imitating redux but easier. Context API is very lacking and similar boilerplate as of redux
> Hooks allow you to reuse stateful logic without changing your component hierarchy.
Why haven't ES7 decorators been more widely adopted for this? We've been using them for over a year for this purpose now and they've ended up being far cleaner, more powerful, and more composable than the proposed interface.
On the positive side hooks provide a lot of options for declarative abstractions. I expect that to a large extent the built-in hooks will be used as building blocks for new libraries. I think it is likely that this will outweigh the negatives.
On the other hand, hooks rely on hidden side effects so that each hook invocation returns something different depending on which component instance in the component tree is being rendered when the hook is called, and the number of times the hook has already been called while rendering that instance. This introduces semantics that are unlike anything I know of in the language. A redditor, u/hardwaregeek, pointed out that hooks behave kinda like a state monad, albeit emulated with imperative side-effects.
I really wish React wouldn't add more state handling, because it's never been particularly good. I use MobX and loathe the overcomplication of stuff like e.g. Redux. Hooks look like a misguided attempt at addressing React's lack of automatic reactivity.
Nice. Its attending to the same *intention as https://github.com/acdlite/recompose but without as much boilerplate (and without HOC which carry some drawbacks)
I'm actually most comfortable with object-oriented programming since I started doing that 26 years ago. However, this simplifies the lifecycle business somewhat and results in a slightly cleaner JavaScript syntax than the class based way in my opinion.
Very cool feature. It reminds me of MobX where changing the observable value auto queues a render. It’s super productive. With hooks calling the setter auto queues a render too.
I’ve had a few embarrassing bugs in the past where the page route changes but it’s the same component so it doesn’t call componentDidMount and doesn’t fetch the new data. It looks like hooks default to fetching data too often instead of not enough which feels correct. Instead of users wondering why the page is broken your team is wondering why the server is getting 1000x more requests.
Front-end devs need to first master the vanilla tools, then reach for the frameworks once they understand the why.
I'd like to see a build tool that focuses on plain HTML, CSS, and ES6 modules. It takes the input .html file, parses the <script type="module"> elements, and statically analyzes the code to polyfill and re-write output for specified browser compatibility.
I think part of the problem is that the intro tutorials for the fully loaded frameworks look so much simpler at a glance than trying to wrangle something on your own.
On the one hand I'm not sure if I like how this paradigm hides it's implementation so thoroughly - abstractions whose core is invisible to the developer is how simple libraries turn into monstrosities.
On the other hand, this will finally make functional component viable, and actually let us get mixin functionality.
I think it's a good idea, maybe even a great idea after a few adjustments, but it doesn't really belong in core IMO.
I like how it makes it possible to re-use behaviour among components, but I also really liked how classes encouraged structure and readability.
My hunch is it's only a matter of time before someone creates a helper that would enable `useState` to take complex objects rather than calling it multiple times.
Also, custom hooks seem almost too powerful - easy to build multiple abstraction layers and make the API feel magical at the point of use.
> My hunch is it's only a matter of time before someone creates a helper that would enable `useState` to take complex objects rather than calling it multiple times.
You can already do that, although it's not _exactly_ the same as setState in a class (states aren't merged together... but if you really wanted to, it'd be about five lines to implement a custom hook)
const [state, setState] = useState({
foo: "123",
bar: "abc",
});
function updateBar(nextBar) {
setState({ ...state, bar: nextBar });
}
Angular's problem with observables is being completely mismatched with component state. You have to unsubscribe stuff manually all the time with any complex app, and it's a nightmare to debug when you've forgotten. You can use `@ngrx/utils` with class-property attributes now, but it's still weird and not always applicable and like most angular things, couldn't be coded by a newbie as it relies on the vomit-worthy-but-kinda-works metadata system.
AFAICT, with state+event hooks you could just write a function in a few lines to subscribe to an observable you pass to it, setState on each new value, and unsubscribe, and then use it everywhere.
(Edit: yes, of course there is the | async pipe. Not everything is in the template though.)
> React doesn’t offer a way to “attach” reusable behavior to a component
I know the angular resentment here is high, but some things Angular 1 got right. Directives added as attributes to another component was very powerful and allowed this in better ways than HOCs in React. Im looking forward to try this out.
I think the Angular resentment is a little behind the times, the days of Angularjs 1 and the beta versions of Angular 2 are behind us. The new Angular is worth a look, especially if react is now having various extras bolted on ad hoc.
> the days of Angularjs 1 and the beta versions of Angular 2 are behind us
There is still a _heap_ of Angular 1 code out there since the 1 -> 2 move basically meant you couldn't upgrade without a full rewrite. Both my last two jobs have involved re-writing Angular 1 apps.
Sure, there are lots of Angular 1 apps still in production, as there will be with most legacy systems. My point is that Angular 1 is no longer the current version, and with the latest version it’s much better to use. I think the main issue is the learning curve, but you just have to bite the bullet.
> A Note from the Author (acdlite, Oct 25 2018):
Hi! I created Recompose about three years ago. About a year after that, I joined the React team. Today, we announced a proposal for Hooks. Hooks solves all the problems I attempted to address with Recompose three years ago, and more on top of that. I will be discontinuing active maintenance of this package (excluding perhaps bugfixes or patches for compatibility with future React releases), and recommending that people use Hooks instead. Your existing code with Recompose will still work, just don't expect any new features. Thank you so, so much to @wuct and @istarkov for their heroic work maintaining Recompose over the last few years.
Everybody is complaining. I like this. Classes should be abolished, though. And perhaps this should be published as a different React version, like "React Classless" or whatever.
Classes with special method names, manual binding of methods, mixed ways of doing things (inlining a function or defining it separatedly?) are a mess already, and this will arguably add more to the mess, but the intentions are good.
It's a huge difference, and yet also the same thing.
It's the same concepts of props, state, context, and lifecycle behavior as before.
It's just that now you can do it in function components, not just class components, and the APIs let you handle things without having to have additional levels of wrapper components and without the complexity of class-related gotchas.
Can someone give me an example where hooks replace a wrapper? All the examples I see can be replaced with a single class, albeit a couple more lines of code.
Context is a great example. Right now, each `<MyContext.Consumer>` instance shows up as another level of wrapping in the devtools, _and_ requires indentation/hierarchy in the render methods. With `const someValue = useContext(MyContext)`, there's no actual component being used, so both the component hierarchy and the render code itself is flatter.
I feel like Redux was a clusterfuck mess and this is the cleanup. Excuse the language.
Edit:
Expanding on this and why (I think) this solution is cleaner:
* In redux you ended up with pointless constant piles of strings that are the named actions. Totally useless in a JS world where a function that IS an action can take up that mantle.
* While reducers are a good way to make state consistent, using redux for state management when you actually don't have 17 different producers and 1 consumer it's major overkill. Most of the time you actually have 1 or 2 event (and value changing) producers and MANY consumers on the screen at one time.
* When you DO need a reducer, instead of making that the norm, this new hook library does provide a useReducer function that works exactly like that- and guess what? NO STRING CONSTANTS YAY.
I think the only downside I can think of is TTDebugging might be complicated to create under this dome, but was the same issue for stateful classes. That being said, it might be more possible now that the control is placed in a single feature area.
People use Redux way too often and think of features like time travel as nice-to-haves, when in reality they're the main reason why you'd want to use Redux in the first place.
Using Redux just to simply set and get data on every page is an overkill.
For the majority of applications making API calls directly from the component and storing data in the comp state is the way to go.
This is coming from someone who used redux excessively in the past.
What do you do when you want to update state in other places in response to a change in a component? How about if you want to make your component's state persistent (particularly when the component is external to your project)? Also, what do you do to generate state dumps for error reporting?
I'll point out that Ryan's demo _did_ show using string constants with `useReducer()`.
Also, our new `redux-starter-kit` library auto-generates action types and action creators for you, _and_ modifies the action creators so they can be used for the `case someAction` comparisons: https://github.com/reduxjs/redux-starter-kit .
Redux is based on the Flux Architecture. In particular, it carries on the idea of describing events or state update requests as plain objects. One of the key insights of Redux is that you can then write additional logic to act on those actions as they progress through a centralized pipeline (ie, middleware).
I've seen lots of Redux-alikes that are "Redux, but without reducers/actions/etc". They mostly claim that they're making things simpler, and in some ways, maybe they do. But, what they're also doing is throwing away the ability to act on those actions as they get dispatched. If I "dispatch a function", I can't programmatically inspect its contents, modify it, log it, attach additional info to it, or all the other myriad of useful things you can do with a plain object.
As for the type strings, you need some kind of useful identifier to distinguish different action objects. You could use Symbols, but those don't serialize well. You could use numbers, but those aren't very meaningful when viewed in an action history log. Ultimately, you want your app's reducer logic, middleware, and the action history itself to be readable and semantically meaningful.
I think then perhaps redux may simply not be for me. I do feel like unfortunately a lot of teams use it anyway, giving React and a few other things a complex feel. When you suddenly take it out of React, at least for simple use-cases, you suddenly feel like React is incredibly simple.
If more teams had been more selective about Redux or to simply use it JUST for global state things like color themes, logged in user, and probably a half dozen other things it might have fewer people burned on it.
I'm definitely not in your shoes - you're a redux maintainer and I do apologize for my "clusterfuck" comment. But I think fewer people would be burned if architects at the top didn't mis-use and abuse. I've been in projects where just about every tiny little input, checkbox, on 30 or so forms had to go through a convoluted redux action. Then when I finally got into my own React usage, without redux, it was like a super-genius baby landed in my lap dancing like it was 1999.
Care to write a substantial contributing comment, or do you intend to keep this random complaint about the simplification of a common pattern in React as shallow as possible?
Edit: No really, I'd like to hear why you think this would make React a "clusterfuck mess" rather than... simpler.
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...