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
You can see the current implementation in [email protected] - see https://unpkg.com/[email protected]/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.
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.
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.
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 :)