Introducing Hooks
reactjs.org
reactjs.org
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...
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 :)
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.
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.
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.
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.
class Foo { handleClick = event => {...} })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...
The real magic is in traversal APIs, like Context
That said, my main point was that "it's only magic for end users" is not really a valid excuse :)
[1] https://twitter.com/VincentTunru/status/1055747566393085952
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 hides internal details, hence users are less familiar with them, hence they struggle more to diagnose when the magic fails to happen.
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.
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.
https://reactjs.org/docs/hooks-faq.html#how-does-react-assoc...
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.
- 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
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.
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.
"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.
https://www.youtube.com/watch?v=kz3nVya45uQ&t=39m (from elsewhere in the comments here)
LOL, did they just block a video presentation for some incidental random music in the background?
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.
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.
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.
Sure, I know what it does and why it is needed, but it is ugly, cumbersome, and I keep forgetting it nonetheless.
- https://www.npmjs.com/package/babel-plugin-autobind-class-me...
- https://www.npmjs.com/package/babel-plugin-transform-class-p...
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
UIBuilder is here: https://github.com/wisercoder/uibuilder
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 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.
<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.
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.
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.
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.
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.
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.
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).
How is static typing "mental laziness"?
``` 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).
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.
Pete Hunt always said he saw React components as mini MVC modules.
it's even less true nowadays with features like Context or suspense to supplement the component states. Redux was kind of a temporary hack.
Found it: https://web.archive.org/web/20140329114924/http://facebook.g...
> JUST THE UI
> Lots of people use React as the V in MVC. Since React makes no assumptions about the rest of your technology stack, it's easy to try it out on a small feature in an existing project.
So I was mistaken. It didn't say that's what it's only intended for, but that's what many people use it for.
Either way, it's not great at the M or C parts of MVC. Context does very little to ease that pain. Redux was more manageable but also overly complex, but it's only React's design that forced it to be so complex.
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.
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.
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.
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.
Simplest react component
// HelloComponent.js
import React from 'react';
export default ({name}) => <div>Hello {name}!</div>
// StatefulComponent.js
import Hello from './HelloComponent.js';
...
return <Hello name={this.state.name} />
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.This. This is spot-on IMHO.
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.
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.
I enjoy my code fairly explicit, so writing a button click that invokes some request has me writing:
- A click handler
- 3 new action name consts (request, success, failure)
- An action instantiation function that takes the necessary arguments for request.
- A saga function that catches these request actions, runs a request, and invokes the success action/failure action depending
- 3 handlers in my reducers. One for every action.
It all adds up if you have a ton of buttons :P
You might want to check out our new `redux-starter-kit` package [0], which can simplify some common use cases for things like action creators and reducers. There's also many other existing libraries to handle repetitive code like API requests as well [1].
[0] https://github.com/reduxjs/redux-starter-kit
[1] https://github.com/markerikson/redux-ecosystem-links/blob/ma... , https://github.com/markerikson/redux-ecosystem-links/blob/ma...
That said, thanks for the recommendations! I’ll check them out.
Also related, the new Redux FAQ entry on choosing between async middlewares: https://redux.js.org/faq/actions#what-async-middleware-shoul...
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.
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.
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.
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.
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.
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.
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 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...
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.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 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.
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.
(defn timer-component []
(let [seconds-elapsed (r/atom 0)]
(fn []
(js/setTimeout #(swap! seconds-elapsed inc) 1000)
[:div "Seconds Elapsed: " @seconds-elapsed])))
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.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.
As it turns, there are only 5 macros, and only a some of those seem to be part of the public API, which is how I found about `with-let` :-)
https://github.com/reagent-project/reagent/search?q=defmacro...
Unless you're willing to migrate to other compile-to-js languages.
Or, if you prefer a business cliche, prefer leading edge over bleeding edge.
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)?
Note they also say:
> We provide a linter plugin to enforce these rules automatically.
Makes me feel a little better.
Like, if I have two state variables, pass in a name for both, instead of relying on the order being the same.
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.
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.
With `useEffect`, I have no idea what is actually happening here, and I've tried re-reading it a few times.
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.
const [foo, setFoo] = useState("abc");
const [bar, setBar] = useState("123");
...is functionally the same thing as const [bar, setBar] = useState("123");
const [foo, setFoo] = useState("abc");
The feature is _declarative_ in the same way element rendering is declarative.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``(); TypeError: Cannot define property foo, object is not extensible
Cool idea though > f = l => l
[Function: f]
> f`hi` === f`hi`
trueIf the goal is helping developers "fall in to the pit of success" I think classes are a much better option than this.
LONG LIVE class based component, there was nothing wrong with them. Don't fix it if ain't broke.
Is that progress? I'm too old to think so.
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.
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.
> 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!
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.
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.
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.
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?
(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.)
Simple example case of a statement that makes it impossible to minify function names:
class Test { static func() { /* function code */ } }
test[prompt('function to call')]()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.
No, but it could be designed around the fact that some of the language features are confusing or crap.
`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.
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.
[1] https://redux.js.org/introduction/ecosystem#higher-level-abs...
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!
[0] https://github.com/markerikson/redux-ecosystem-links
redux-starter-kit looks great too!
export default connect(mapState, {addTodo, toggleTodo})(TodoList);
We're about to add a whole big section on dispatching and action creators to our new React-Redux docs site at https://react-redux.js.org - keep an eye out for that. Until then, check out Dave Ceddia's post at https://daveceddia.com/redux-mapdispatchtoprops-object-form/ for an explanation.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.
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.
This is going to make it about a million times easier to use Typescript with React.
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.
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?
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.
1. https://babeljs.io/docs/en/babel-plugin-proposal-class-prope...
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
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.
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.
https://github.com/acdlite/recompose/blob/master/docs/API.md
[1]: https://medium.com/@jrwebdev/react-higher-order-component-pa...
I guess this is the natural progression of frameworks. What was once simple will get complex until the next thing comes around.
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.
That's a rather un-generous description of a framework under active development with an active community.
Though I'm happy that mitrhil.js lives and is being developed (not just maintained). I only wish it wider adoption.
The "not completely dead" was meant to be somehow tongue-in-cheek.
If it turns out to be a bad idea (hypothetically speaking, I haven't read the article yet so I don't know), will it be backed out?
Wouldn't it make more sense for it to be versioned as an experimental fork/branch instead?
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.
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.
> In modern browsers, the raw performance of closures compared to classes doesn’t differ significantly except in extreme scenarios.
https://reactjs.org/docs/hooks-faq.html#are-hooks-slow-becau...
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.
Order-based management could then be opt-in, and based on a non-magical ID-based system.
[ get, set ] = getterSetter(defaultValue)
as a wrapper around some stateful, immutable value (mutating the result of the get won't be tracked, that is)
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:
const [getter, setter] = this.useState(...)
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!
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.
There is no decorator feature in the ES7 (ES2016) spec.
However, there is a proposal for decorators that it is not finalized yet.
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.
Now get off my lawn while I tend to my rum*
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.
Looks like one of those things you have to use for a couple of days before coming to a conclusion.
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.
Any ideas for youmightnotneedreact.com?
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.
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.
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 });
}The call order thing seems odd. Don't know how to sell this to people.
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.)
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.
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.
If so, colour me excited!
Use Custom Hooks with React Class Components. Compatible React >= 16.0.0
I really don't see the point in hooks.
> It’s hard to reuse stateful logic between components
Recompose kind of solves this. Why add this to the core API? What am I missing?
For reference, it says:
> 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.
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 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.
Edit: No really, I'd like to hear why you think this would make React a "clusterfuck mess" rather than... simpler.
https://github.com/facebook/react/pulls?q=is%3Apr+is%3Aclose...
Even the developers have no idea WTF is going on.
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.
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 .
https://reactjs.org/docs/hooks-reference.html#usereducer
To something without strings and hard-coding the actual "reduction code" to something like
export const increment = (state) => ({count: state.count + 1});
And give modern JS languages like typescript import the increment function and use it directly?
Why are we so attached to these action type strings?
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.
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.
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.
In 90% of the cases one of the parent components. Sometimes redux when the component tree goes super deep, or for things such as currentUser.
> How about if you want to make your component's state persistent (particularly when the component is external to your project)
What do you mean? Give me an example.
> Also, what do you do to generate state dumps for error reporting?
I don't generate state dumps for error reporting.
I remember when it was AWESOME, back in 2013 when only me and like 10 people were using it and the API was simple because it was brand new.
ugh, now I have to read a bit more and have all these new features. I'm an engineer, I only like things when they're simple.
I wish it was back in the simpler days when JQUERY was hot. Man, miss those days for sure.
https://reactjs.org/docs/hooks-faq.html#how-to-test-componen...
What exactly looks difficult about testing them? Existing components already carry state and use lifecycle methods...