- When I first started using React, it was unclear what role
useRef
played, outside of providing access to a DOM node when used with the ref
attribute. The important distinction is that useState
triggers a re-render, while useRef
doesn't, so they can really be thought of as two ways of maintaining local state. - Inside
useEffect
, you can return a function which runs whenever the component unmounts. This is super helpful for canceling pending async requests, or cleaning up any state which might be effected by an unmount. - Use custom hooks liberally. There's really very little downside if they contain some stateful logic that's used in multiple places.
- Sometimes, you need to trigger a re-render when an external event happens, like a window resize. The right way to do this is to create a hook with an event listener, and have the listener update some internal state, so even if you don't care about the size of the window, just having
useViewportSize()
in your component will make sure it always re-renders as needed. - Sometimes, you want to call
setState
without having access to the current state, either because it's not in scope, or because you're in a stale closure. setState
allows you to pass in a function which receives the previous state as an argument. For instance, if you have [showLabel, setShowLabel]
and you want to toggle the value, you can call setShowLabel(prev => !prev)
to flip it. - You should use Contexts sparingly. They add cognitive overhead and tightly couple your components. However, they're invaluable when several components need to share state. My rule of thumb is that I'll consider a context if I'm passing a prop down more than one level (
prop drilling
), but I'll almost always use one if I'm passing a prop down more than two levels. - Let's say you have a
UserContext
, it can be helpful to export a useUserProvider
hook from the same file. That way, instead of having to import { useContext }
and import { UserContext }
, you can just do import { useUserProvider }
and save some typing.