DEV Community

Devin Rasmussen
Devin Rasmussen

Posted on

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.

Top comments (0)