Location Boston, MA
Interests Programming, Photography, Cooking, Game Development, Reading, Guitar, Running, Travel, Never having enough time...
This Site Made with Astro, Cloudinary , and many many hours of writing, styling, editing, breaking things , fixing things, and hoping it all works out.
Education B.S. Computer Science, New Mexico Tech
Contact site at dillonshook dot com
Random Read another random indie blog on the interweb
Subscribe Get the inside scoop and $10 off any print!
A Critique of React Hooks Addendum
This is a follow up to A Critique of React Hooks which blew up beyond expectations and generated a ton of discussion that I enjoyed reading. If you haven’t read it yet please take the detour before spoiling the quiz for yourself! I’m not one to harp on a subject repeatedly but after seeing all the quiz results come in I thought it would be valuable to share them as well as a couple of thoughts across common themes in the discussion.
Quiz Results
With over 2,600 responses at the time of writing this is the biggest sample size of an unscientific quiz I’ve ever given! Even ignoring the last question which was mostly there for laughs, the results were still very interesting. The median score is 1 out of 4 questions despite 3/4ths of the questions having a correct popular vote. To me this indicates that people are familiar with a few of the hooks but haven’t used all the ones included in the quiz.
Quesiton 1
The first and simplest question had the highest percentage of correct answers to no surprise. However, over 40% of you were surprised that the Child
’s useEffect
fires before the Parent
’s after rendering both components.
Question 2
This one surprised me when I first ran it but makes sense when you take a minute to think about it. Both useLayoutEffect
’s run before the useEffect
’s and they run from the bottom of the component tree upwards. Hopefully you’ll never get into a situation where this particular ordering matters though since only 30% were able to predict the order at first glance.
Question 3
This question has the intentional bug of creating a new object every rerender and passing it to the child as a prop which causes the memo to run every rerender which a third of you caught. It would be interesting to compare these results with a question around a similar bug of creating a function inside the render and passing it to the child which causes the Child to rerender every time the Parent does:
function Parent(props) {
function doSomething() {
// … props …
}
return <Child onSomething={doSomething} />
}
This is fixed with a useCallback
hook, and the quiz problem is fixed by either updating the object via useRef
or refactoring the props of the child to not take an object as a prop. Both of these scenarios can come from refactoring a class component where you either have a class method or property that don’t get recreated every render. When converting you have to keep this in mind and take the obfuscation penalty wrapping them with hooks.
Question 4
This is the opposite sort of bug where a Child
component needs to be rerendered but isn’t. It happens when useRef
is needed to maintain a stable reference over rerenders but then a child component is added that needs the value. If the whole ref is passed as a prop instead of the .current
value the Child
component won’t be rerendered because the ref hasn’t changed. In isolation this shouldn’t be too hard to spot but can be tricky when there are many props being passed from different places to a component.
A few more thoughts
On More Stuff to Learn
There were a few comments about how easy hooks are to learn. While I would agree that reading the docs and getting the gist of how hooks work doesn’t take too long, fully understanding their ramifications, and the bugs you’ll run into, will be an ongoing process. The quiz results show that even if you got everything right, you’ll likely be working with someone that didn’t.
Adding more to learn just raises the barrier to entry. And every developer should be trying to remove as many barriers to entry because it’s in our best interest to do so.
They Don’t Compose with Themselves
Programming languages are powerful because they give you building blocks that you can compose together in many different ways. If you have the blocks loop
and function
, all the possible combinations are valid including nesting. loop
in loop
, loop
in function
, function
in loop
, etc. Hooks don’t have this property. You can’t use a hook inside a hook (excluding custom hooks that are just wrapper functions for the native hooks). You can’t use a hook in a loop. So when you go from having one thing handled with useRef
to needing multiple things handled by useRef
you can’t just stick it in a loop or array like you would with any other language building block. You have to redesign it to work with hooks.
Why Control Flow Matters
When you inevitably get to debugging, you should start with a mental model of what you expect to happen including all the assumptions about what state you’ll be in throughout the process. Whenever you step or log your way through and one of your assumptions is broken, you know you’ve found a piece of the bug. Now you just have to figure out why your assumption was wrong.
Between the hook execution order and figuring out why a component rerendered it can be very difficult to create that mental model of what’s supposed to happen.
Last Words
Whether you like hooks or not, I hope through these posts you’ve learned something even if it isn’t as general as I’d like :)
As always, Thanks for reading and discussing!
Lots of comments also posted over on Hacker News https://news.ycombinator.com/item?id=23102639
Want the inside scoop?
Sign up and be the first to see new posts
No spam, just the inside scoop and $10 off any photo print!