3 ms·
I've toyed with hooks for a few hours and, even if I understand why they're focusing on teaching the basic pitfalls (dependencies arrays mostly), I don't think
by kaoD 8y ago
I've toyed with hooks for a few hours and, even if I understand why they're focusing on teaching the basic pitfalls (dependencies arrays mostly), I don't think they're the most confusing part (as long as you know how closures work, i.e. as long as you know JS).
The real deal is `useEffect` vs `useLayoutEffect`. It seems easy, but it's not. When should I use each? According to the docs, use `Layout` if you interact with the DOM for measurement or whatever (I mean, look at its name)... WRONG!
I made a custom `useFetch` hook that uses `useEffect` to re-fetch when the URL changes. It should be fine, right? Every other `useFetch` I've seen on GitHub does that too... And it's wrong.
I noticed that as soon as I added a second `useFetch` that fetches depending on the first's data, there is flashing in the UI. Since `useEffect` is asynchronous, there's a small gap where both internal states can be desynchronized (completely unrelated to the promise, I mean the internal `isLoading`, etc.) `useLayoutEffect` solves the issue (since it blocks the rendering until both effects are finished updating their internal states) but then... my internal hook implementation affects the external behavior in non-obvious ways!
I don't know whether my hook consumer needs to block the UI until all updates finish, so I actually need to make two different `useFetch` and `useSyncFetch` hooks, or just ditch the async one.
It's definitely a footgun. I predict this is going to bite everyone's ass. It's also not really well understood (try searching for "useEffect vs useLayoutEffect") and the React team is completely ignoring the issue. I haven't seen it mentioned anywhere, and I can't believe I'm the only one that noticed it (I guess hooks haven't been used that much yet?)
The issue is exacerbated by `useEffect` being the "default" in the docs, while `useLayoutEffect` is explained later as an option (even if they admit that it's the safest option if you're migrating from classes).
I recommend sticking to `useLayoutEffect` unless you're absolutely 100% sure your consumer does not need to block the UI (when can you be 100% sure?)
There's a similar thing with `useState` vs `useRef` for mutable state (where the only difference is that `useState` triggers re-renders) but since `useState` is presented as the default (re-render even if you don't know if for consumer needs to) it does not seem like that much of a footgun (you have to reach for `useRef` if you really need it, e.g. in the infamous `useInterval` Abramov's blog post). It's also very similar to how state vs. instance variables worked, and it's very obvious how `useState` interacts with React (completely different behavior unlike `useLayoutEffect`, which is the same but in different parts of the component lifecycle).
When your internal hook implementation affects your component's behavior in non-obvious ways, there's something wrong with the hooks API. As proof, see how many custom `useFetch` hooks use `useLayoutEffect` when they should. Zero. And I'm sure there are many more instances of this problem hidden in other custom hooks (it's hard to tell, since it's not obvious until you use a hook that depends on other hook's output and see the flashing).
- danabramov 8y agoDo you want to file an issue? I’d be happy to answer some of these questions and maybe improve the docs. useLayoutEffect is not the recommended way to do it. You’re probably missing a simpler solution like setState during render. But I’d need to see the code to be sure, and GitHub issue would be a better place to do it.
- kaoD 8y agoDefinitely, it crossed my mind, I just have not had enough time to write an minimal bug example unfortunately :/ In this case I think the problem is `setState` is inside my `useEffect` and I'm using its dependencies array as a sort of substitute to `componentWillUpdate` + compare `props` to `prevProps` (hence the async state update). In class-based react I would just atomically `setState` both states and there would be no problem, but since hooks allow you to modularize behavior a new type of data dependency appears (there are multiple states in a single component) making it harder to reason. Thanks for chiming in, will file an issue ASAP.
- danabramov 8y agoNo problem, I'm sorry for the confusion. I think your use case might be solved by this approach: https://reactjs.org/docs/hooks-faq.html#how-do-i-implement-getderivedstatefromprops https://reactjs.org/docs/hooks-faq.html#how-do-i-implement-g...