The magic is usually what gets to me. Stuff like useState depending on the order it was called under the hood, meaning you have to use it a specific way.
On the one hand, it really does clean some things up well, like for higher order components, but also I find the magic just leaks more than I’d like, and as a result I feel like the only way use the magic safely is to study all of the pitfalls.
It’s like using C++ smart pointers but a bit less foot-shooty.
I have seen a lot of crappy blogs that try to explain how useState works and they were all highly misleading. They try to explain things with closures and other random made up stuff.
The code isn't simple but the way it works is much simpler than what these blogs show.
1. React uses a bunch of global variables during rendering to make the current component and its hooks available to a global function (useReducer). Basically a react specific "this"
2. Hooks are stored in a linked list, meaning the first hook that is called gets the first hook's data. If you have hooks A,B,C and disable B then C will get B's data.
3. The way hooks themselves work is that all hooks call useReducer and the hook state is just a queue that is fed into the reducer which is called whenever useReducer is returning the latest state as the first returned value.
4. The second returned value is just a function that pushes a new entry into the queue.
While this is kinda weird, I didn't find the dependence on the order of the hooks problematic. The most natural way to use hooks is also the correct one, at least for me.
What is a bit tricky is useEffect, but I found that in many cases I either didn't actually need it, or I can use libraries for common tasks that hide the more complex parts. Using react query for data fetching simplified those parts considerably, and I never have to write a useEffect to fetch data from the server.
First of all: useState is not a pure function, nor is setState. I would think that’s obvious from the name. The order isn’t hidden and it’s not magic, it’s the order they’re defined in the function body, because under the hood the React runtime is storing all the hooks you call in an array and firing them off by iterating through the array. It is a very conventional architecture in event handlers: of course there is going to be an order they get called in, there is no way to avoid it. But it’s perfectly explicit.
>because under the hood the React runtime is storing all the hooks you call in an array and firing them off by iterating through the array.
I must correct this. Pay attention to this function [0]
It's just a plain linked list and nothing is being fired off by iterating through the linked list. When you call a hook react either creates a new node in the linked list or it pops the current head off the linked list so that the second hook will get the second element, the third hook the third element and so on.
The hooks are fired off by calling them. React isn't calling them again. Each render simply calls your component function and your function calls the hooks which runs a reducer [1] on the internal queue of the hook to compute the latest state. The second return value is just pushing elements into the internal hook queue.
Yes, you're right. Iterating is not the right word, and it's the ordering of the data in the internal queue that I was thinking of, which is fixed by the order in which the hooks are called. During rendering, your component itself will run through the queue. It's not React doing it.
On the one hand, it really does clean some things up well, like for higher order components, but also I find the magic just leaks more than I’d like, and as a result I feel like the only way use the magic safely is to study all of the pitfalls.
It’s like using C++ smart pointers but a bit less foot-shooty.