JavascriptLoser
JavascriptLoser

Reputation: 1962

React.useMemo does not work to prevent re-renders

I have the following simple component:

const Dashboard = () => {
  const [{ data, loading, hasError, errors }] = useApiCall(true)

  if (hasError) {
    return null
  }

  return (
    <Fragment>
      <ActivityFeedTitle>
      <ActivityFeed data={data} isLoading={loading} />
    </Fragment>
  )
}

export default Dashboard

I would like to prevent ALL re-renders of the ActivityFeedTitle component, so that it only renders once, on load. My understanding is that I should be able to use the React.useMemo hook with an empty dependencies array to achieve this. I changed by return to be:

return (
    <Fragment>
        {React.useMemo(() => <ActivityFeedTitle>, [])}
        <ActivityFeed data={data} isLoading={loading} />
    </Fragment>
)

As far as I'm concerned, this should prevent all re-renders of that component? However, the ActivityFeedTitle component still re-renders on every render of the Dashboard component.

What am I missing?

EDIT:

Using React.memo still causes the same issue. I tried memoizing my ActivityFeedTitle component as follows:

const Memo = React.memo(() => (
  <ActivityFeedTitle />
))

And then used it like this in my return:

return (
    <Fragment>
        {<Memo />}
        <ActivityFeed data={data} isLoading={loading} />
    </Fragment>
)

Same problem occurs. I also tried passing in () => false the following as the second argument of React.memo, but that also didn't work.

Upvotes: 8

Views: 16483

Answers (5)

Aaron Ross
Aaron Ross

Reputation: 1

It's because rendering a parent causes it's children to re-render for the most part. The better optimization here would be to place your data fetching logic either in the ActivityFeed component or into a HOC that you wrap ActivityFeed in.

Upvotes: 0

Amanda Hewitt
Amanda Hewitt

Reputation: 11

The second argument passed to React.memo would need to return true in order to prevent a re-render. Rather than computing whether the component should update, it's determining whether the props being passed are equal.

Upvotes: 1

ibtsam
ibtsam

Reputation: 1720

You can use React.memo and use it where your define your component not where you are making an instance of the component, You can do this in your ActivityFeedTitle component as

const ActivityFeedTitle = React.memo(() => {
  return (
   //your return
  )
})

Hope it helps

Upvotes: 1

Joseph D.
Joseph D.

Reputation: 12174

Use React.memo() instead to memoized components based on props.

React.memo(function ActivityFeedTitle(props) {
  return <span>{props.title}</span>
})

Take note:

This method only exists as a performance optimization. Do not rely on it to “prevent” a render, as this can lead to bugs.

Upvotes: 2

Karthik R
Karthik R

Reputation: 5786

Your usage of useMemo is incorrect.

From react hooks doc:

Pass a “create” function and an array of dependencies. useMemo will only recompute the memoized value when one of the dependencies has changed. This optimization helps to avoid expensive calculations on every render.

If no array is provided, a new value will be computed on every render.

You need to use useMemo like useEffect here for computation of value rather than rendering the component.

React.memo

React.memo() is the one you are looking for. It prevents re-rendering unless the props change.

Upvotes: 0

Related Questions