A Work in Progress

developer's blog by Wei
lots of work in progress notes
TimelineCredits

Properly Include Component Function from useEffect and useCallback Dependency List

October 12, 2019

I was working on Slots (letting a portal component render into a potentially not mounted portal home) when initially I was violating a hooks rule. Namely, I did not include the setSlot function I called within the useCallback body inside its dependency list.

export const SlotHome = ({ slotName, className }) => {
  const [, setSlot] = useSlot(slotName)
  const ref = React.useCallback(node => {
    // we're lying to hooks because we're using setSlot without including it in the deps list
    setSlot(node)
  }, [])
  return <div className={className} ref={ref} />
}

But if I do include it, the function is different every time the component render is called and therefore would re-render like a maniac. Without including it, the implementation still works, but we're lying to hooks and it is strongly advised against.

This made me finally decided (cuz it says it's a 49-min read) to read Dan Abramov's article, A Complete Guide to useEffect and decided to properly figure this out. Here are some key takeaways:

  • class components mutate state
  • functional components + hooks retain the proper closure behavior (always use closed variable, no mutating crap)
  • 3 ways to optimize dependency list

    • remove unnecessary use of params in component lifecycle scope
    • use reducer
    • memoize functions with useCallback

The highlighted takeaway was what solved this problem. The idea is to tell hooks that the function won't change unless what we specify in that useCallback's dependency list change.

const [, setSlot] = useSlot(slotName)
// specify that setSlotMemo won't change unless slotName changes
const setSlotMemo = React.useCallback(setSlot, [slotName])

// now ref is safe to include setSlotMemo in its deps list
const ref = React.useCallback(
  node => {
    setSlotMemo(node)
  },
  [setSlotMemo]
)

Once again Dan's article covers a complete guide to properly use useEffect (which extends to useCallback because they share the same signature).

Relevant links