5 ms·
(I edited the post title to include “React” before the “Hooks” to disambiguate. Might be worth editing the submission too!) Hope you’ll enjoy reading it.
by danabramov 8y ago
(I edited the post title to include “React” before the “Hooks” to disambiguate. Might be worth editing the submission too!)
Hope you’ll enjoy reading it.
- strife25 8y agoJust want to say that I've been loving your posts so far. Well written and I love the perspective of exploring _why_ an API is designed the way it is instead of trying to focus on explaining why <insert library> is the best for <insert language>. There are only a handful of blogs I've read over my career that I feel like focus on solving real application development problems, exploring trade-offs, or digging into why an approach was taken and these posts are of that caliber. Those topics help me in the problems I encounter frequently in my day job and make me a better engineer. Thank you for the time and effort you're putting into this.
- rattray 8y agoThanks, this was fun. Enjoying your "unofficial" posts.
- politician 8y agoLooking at the design, it's clear that the team is trying really hard to make functions look and work like classes look. Variable declarations at the top, followed by helper methods, followed by lifecycle methods. Given that classes are themselves sugar to make prototypes palatable to a broad range of developers, it's certainly a tough design task; I'm not convinced that reliance on call-index is wrong per se, but it is magical (and magic in JavaScript is obnoxious). That said, the presentation of the third and forth flaws seem a bit weak. useState accepts a key as the first parameter, but in both of the composite functions that input parameter disappears. That looks like a refactoring error. From a design standpoint, if `useState(symbol)` is acceptable (and I'm not saying it is), then `useWindowWith(symbol)` would likewise be acceptable in that it's not adding any more requirements to the interface than the vanilla version. Proper bookkeeping of symbols resolves the issues here. However, if the design objective is to reduce the effort on the developer to do their own bookkeeping, then call-order indexing does make sense. (Not to mention that Symbols are not supported by IE11 which remains an important target for bigger companies.) I'm not sure there's a good way out. When you look at solutions like Protocol Buffers, they just bite the bullet and require the developer to supply the indexing. If JavaScript had something like Go's iota, then you could imagine using an enumeration to supply the indexing without requiring everyone to type 1,2,3,4... etc. But it doesn't so, that's wishful thinking. Nevertheless, the react codebase itself does contain a giant list of assignments of Symbol() || number to constants, so it's a pattern you're already aware of. A tough nut to crack.
- danabramov 8y ago>From a design standpoint, if `useState(symbol)` is acceptable (and I'm not saying it is), then `useWindowWith(symbol)` would likewise be acceptable in that it's not adding any more requirements to the interface than the vanilla version. I think you're missing that `useWindowWidth()` could have more than one `useState()` and thus you'd need to somehow compose Symbols. Which is what the next section is about.
- danabramov 8y agoIn particular, I would suggest to take the `useSubscription` example from "diamond problem" section and try to convert that whole snippet. You'll see where it falls apart.
- politician 8y agoI must be missing something because this didn't fall apart when I refactored it to work with a `useState(symbol, value)` interface. I'm not saying this is a good or ideal solution, indeed if the useWindowWidth hooks were to both useState and also useSubscription, then multiple keys would have to be injected -- it would work correctly, but the interface would have the AMD feel that perhaps is undesirable (per flaw #8). https://gist.github.com/politician/5f03c169a4119a63abb785b1c1249656 https://gist.github.com/politician/5f03c169a4119a63abb785b1c... EDIT: I still think it's a hard design problem and the concerns in flaw #8 are _very real_ to a broad range of developers. When you're just trying to get it done, proper bookkeeping of Symbols and injecting them into hooks composed of other hooks might cross the line. It's a shame that relying on call-order indexing is the solution because it's magical. But at the end of the day, engineering is about trade-offs. Time will tell whether deeply nested composition of automatically-managed hooks was a good feature to expose.
- WorldMaker 8y ago> It's a shame that relying on call-order indexing is the solution because it's magical. I've been thinking about this a lot since Hooks were introduced, and I'm increasingly of the opinion that it isn't that magical. Order of operations is incredibly important in the functions we write, especially in a language like JS that makes no effort for strict "pure" side-effect free functions. The order of a console.log or a return versus an increment matters in JS. We write a lot of procedural code in JS where order matters (a lot) already. In that matter, Hooks can just melt into the "procedural" background of JS. That said, I still feel like I want a better solution than "lint errors" for things like accidental branches of a Hook. I don't have any more of a proposal for how that would work or what that would mean than the article here, though, unfortunately.
- kmarc 8y agoI just briefly looked at the examples (and of course followed the twitter boom about it), but what I am not getting is that to me it looks like since useState() is against the principles of pure functional programming. I understand that it implements some kind of inversion-of-control, but just looking at the code it feels like using some global object's method which is a big no-no. Also this importance of ordering reminds me the unmaintainable magic hell of Angular. Maybe I'm just missing the "explicit-over-implicit" concept here. What's your opinion on this?
- danabramov 8y agoI suggest you to read about algebraic effects. We don't have them in JavaScript, but conceptually that's what Hooks represent. (This is as functional as it gets.) React has always been about taking useful ideas from functional programming and bringing it to mainstream through pragmatic choices in JS. Your concern about the "globalness" is addressed in Sebastian's comment which is linked five times throughout the post — you should definitely check it out! https://github.com/reactjs/rfcs/pull/68#issuecomment-439314884 https://github.com/reactjs/rfcs/pull/68#issuecomment-4393148... Finally, don't forget you're comparing Hooks to classes. Those are hardly functional either.
- asark 8y agoIt's not a global object—it's an instance of the object for the component you're using. The whole thing's a terrible OOP system in a language that already has a built-in mediocre OOP system, which terrible OOP system is, in the end, just a(n inefficient) pass-through to same built-in mediocre OOP system. The posters here wondering why you can't name them are on to something. I assume it's because then it'd be too obvious that's what they're doing, and whoever's paying people on that team (I really hope it's not more than one person, it's not hard work, but it probably is) might notice and make them stop, and maybe the React team at FB would even shrink in size, and we can't have that. I'm not sure what other explanation there could be for such comically-wasteful sandcastle building. I assume it's a combination of individual incentives to work on something not-difficult but flashy and prominent, with project incentives to never need fewer people than they currently have.
- drekembemutombo 8y agoEnjoying your posts, keep them up! Hooks look like a very powerful API for sharing pieces of functionality between components. You've probably heard this one a lot, but what bothers me a bit about hooks (especially useState) is that before, when you saw a component defined as a function, you could assume that it was just a function that renders something based on its props. However, I think it's a fair trade-off for such a powerful API.
- WorldMaker 8y agoRecent versions of React have encouraged truly pure props-only functions to wrap them with React.memo(), which also adds the benefit of giving them the equivalent of a shallow-check `shouldComponentUpdate()` avoiding re-renders when the props are the same. React.memo() is all the more useful of a signal in the Hooks world.