DEV Community

Devin Rasmussen
Devin Rasmussen

Posted on

2 2

useMemo in an application

There are a few hooks exposed by react that I don't reach for that much (if ever). This probably says more about my preferences and the applications I work on than the utility of the hook, but today I found an itch that useMemo scratched perfectly.

I wrote a fairly complex custom hook with Observables upon Observables that served the purpose of firing and organizing multiple network requests. I'm increasingly impressed with RxJS but that's not the purpose of this post.

Here is a simplified version of the custom hook.

export default function useGetTodoCountPerContact(contacts) {
  const [contactCounts, setContactCounts] = useState()
  const getSomeItems = () => networkRequestAsObs()...
  const getOtherItems = () => networkRequestAsObs()...
  const getCounts = id => forkJoin(getSomeItems(), getOtherItems())
    .pipe(map(([a, b]) => ({ [id]: a + b }))

  useEffect(() => {
    if (contacts) {
      forkJoin(contacts.map(contact => getCounts(contact.id)))
        .subscribe(res => setContactCounts(res))
    }
  ), [contacts] }
  return contactCounts
}
Enter fullscreen mode Exit fullscreen mode

I proceed to consume this custom hook and everything is looking good.

const contactsTodos = useGetContactsTodosCount(contacts)
Enter fullscreen mode Exit fullscreen mode

Then I realize the data includes the current contact. I need to filter that out because I already have the data for that particular contact. No problem.

const contactsTodos = 
  useGetContactsTodosCount(contacts.filter(c => c.id !== activeContact.id))
Enter fullscreen mode Exit fullscreen mode

But now I start to notice something interesting in the network tab.

Alt Text

It appears to be an infinite loop!

When the custom hook returns its updated state, my consuming component is re-rendering and then telling the custom hook it has a new filtered list of contacts.

In order to make sure that react doesn't think there is a change in filtered contacts, I moved this filtering into a variable that is set with useMemo()

const excludeCurrent = 
  useMemo(() => contacts.filter(c => c.id !== activeContact.id), [contacts])
Enter fullscreen mode Exit fullscreen mode

Now react is not diffing an array that is calculated on every render but is diffing the contacts from props which does indeed remain the same.

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay