panzerdp / dmitripavlutin.com-comments

7 stars 0 forks source link

/use-react-memo-wisely/ #34

Open panzerdp opened 3 years ago

panzerdp commented 3 years ago

Written on 07/10/2019 12:24:16

URL: https://dmitripavlutin.com/use-react-memo-wisely/

panzerdp commented 3 years ago

Comment written by Robert Cooper on 07/20/2019 06:59:10

Great read! Where did the following quote come from?

Performance-related changes applied incorrectly can even harm performance. Use React.memo() wisely.

I'm curious to read more about performance implications of over using memoization in React applications.

panzerdp commented 3 years ago

Comment written by Dmitri Pavlutin on 07/20/2019 08:13:08

Thanks @disqus_t8zxzdWNo5:disqus!
The quote you're mentioning is mine. It's a key takeaway.

I will think about writing a post that measures the performance gains of memoization.

panzerdp commented 3 years ago

Comment written by Young Moon on 07/25/2019 01:02:09

@disqus_t8zxzdWNo5:disqus
Maybe in case of using memoization for regularly update with new props can lead to useless comparison costs.

panzerdp commented 3 years ago

Comment written by Robert Cooper on 07/25/2019 01:03:49

Yes, perhaps. Good point.

panzerdp commented 3 years ago

Comment written by Michael on 07/26/2019 02:14:41

Of course, you cannot use React.memo() on non-pure components, e.g. components that have state

I don't think this is true. Memoised components which have internal state (via `useState`) will still re-render if that state changes, even if their props have not.

panzerdp commented 3 years ago

Comment written by Michael on 07/26/2019 02:50:44

With the default comparator, the comparison cost should be negligible, even with a large number of props.

panzerdp commented 3 years ago

Comment written by Dmitri Pavlutin on 07/26/2019 07:03:02

I think it's not correct to ignore memoization equality check costs. That's a code smell and would lead to inappropriate use of memoization.

For a typical use case of your component, you should use profiling to determine whether the component performs faster with memoization or without it. The outcome depends on how often your component re-renders with the same props.

panzerdp commented 3 years ago

Comment written by Dmitri Pavlutin on 07/26/2019 07:27:23

You're right, it's not a mistake to use state hook with memoization. Post updated.

panzerdp commented 3 years ago

Comment written by Porfírio Ribeiro on 07/26/2019 08:15:12

I use that with components that useContext and do lots of stuff when context change, but i don't need them to update when parent change because of some unrelated state.

Works pretty good

panzerdp commented 3 years ago

Comment written by andrepcg on 08/03/2019 09:53:03

4. Why not just do the following without having to use `useCallback`?

`onLogout={cookies.clear}`

panzerdp commented 3 years ago

Comment written by Dmitri Pavlutin on 08/03/2019 15:44:51

Because you need to call cookies.clear() without arguments.

panzerdp commented 3 years ago

Comment written by andrepcg on 08/03/2019 16:25:43

Since you don't go into details about the `cookies.clear()` function and the way it is called, I assumed calling it with or without arguments would be the same. But with that new constraint (must be called without arguments) then you are right

panzerdp commented 3 years ago

Comment written by disqus_dmflxzVdEa on 08/18/2019 09:21:50

In the useCallback snippet you provide an empty dependency array, while you are referencing the cookies prop in the function. So if the cookies prop changes at any time, the function returned by useCallback will still have a reference to the cookies received on mount. You should add cookies to the dependency array like this: const onLogout = useCallback(() => { cookies.clear() }, [cookies]);. This way useCallback will compare the previous dependencies to the current ones, and if any of them have changed, it will return a new function which closes over the current dependencies.

panzerdp commented 3 years ago

Comment written by Dmitri Pavlutin on 08/19/2019 08:34:49

Thanks for the detailed description!

I consider `cookies` prop a singleton (or a namespace) that does not change during runtime. That was my initial though regarding empty dependency of useCallback().

But now I see the need to clear the reference when `cookies` changes. This might happen during testing since I would like to use multiple mock instances of `cookies`: to check whether cookies are cleared on logout.

Post updated.

panzerdp commented 3 years ago

Comment written by Joshua Barker on 10/04/2019 14:16:51

In your examples, you only every use your memoized component as a single instance... I'm confused and have not been able to find an answer to this question... is the MemoizedComponent returned by React.memo(fn) a singleton like reselect's createSelector function, or can it be used to create multiple, independent memoized component instances?
Example: Is this valid?

// MovieList component
render() {
const { movies } = this.props;
return (
<div>
{
movies.map((movieData) => {
return (<memoizedmovie {...moviedata}=""/>);
});
}
</div>
);
}

or can MemoizedMovie only ever be used as a single instance?

* this code editor sucks...

panzerdp commented 3 years ago

Comment written by Dmitri Pavlutin on 10/04/2019 14:49:48

Think of the memoized component exactly as the original one, only that internally it will not re-render if the props are the same.
You can use the memoized component anyhow you like, including the creation of multiple instances.

panzerdp commented 3 years ago

Comment written by Namrata Saun on 02/19/2020 08:35:16

You can do that. Functional components are stateless (this is practically not true), usually.
The 6th point clearly states that you can use hooks and they will work normally. It's like extending PureComponent just with optional conditions

panzerdp commented 3 years ago

Comment written by Cihat Imamoglu on 06/03/2020 20:55:23

This was a very well-written article, thanks a lot!

panzerdp commented 3 years ago

Comment written by Dennis Felyx on 10/02/2020 17:18:12

Hello,

I'm confused by that statement: "If the component doesn’t re-render often with the same props, most likely you don’t need React.memo()."

Example scenario: show a list of names and allow to change them.

List consists of multiple list items (each is a separate component).

Each list item is wrapped in React.memo and accepts a name and a callback (which in parent is wrapped in useCallback).

Each time I edit list item #3, all other are re-rendered. I think it's ok to use React.memo with list item component.

What do you think?

panzerdp commented 3 years ago

Comment written by Dmitri Pavlutin on 10/14/2020 08:02:25

Hey Dennis,

You need to profile the performance of the component with and without React.memo(), then decide by yourself is the performance gains worth it.

peterczg commented 3 years ago

Thanks a lot for your brilliant post, now I just understand more about react re-rendering and how to optimize my pure functional component with React.memo.

panzerdp commented 3 years ago

Thanks a lot for your brilliant post, now I just understand more about react re-rendering and how to optimize my pure functional component with React.memo.

You're welcome @peterczg.

Gleb-Gaiduk commented 3 years ago

Thanks, great article.

ecatugy commented 3 years ago

Great explanation, Thanks for sharing your knows.

panzerdp commented 3 years ago

Great explanation, Thanks for sharing your knows.

Thanks @ecatugy!

danielmariz commented 3 years ago

I wonder if all stateless ui components built with using styled components should be exported wrapped in react memo

panzerdp commented 3 years ago

I wonder if all stateless ui components built with using styled components should be exported wrapped in react memo

Profile and measure the performance gains (if any), then decide to use memo or not.

roanjain commented 3 years ago

React always re-renders the component if the state changes, even if the component is wrapped in React.memo()

Even if the props has not changed?

panzerdp commented 3 years ago

React always re-renders the component if the state changes, even if the component is wrapped in React.memo()

Even if the props has not changed?

Yep.

maminataei commented 3 years ago

Thanks .. It's awesome

jvinhit commented 3 years ago

Thanks .. It's awesome

windmaomao commented 3 years ago

Thank you for the article. I do have a question.

Does the section "3.1 Useless props comparison" mean that even with the props comparison, it wouldn't be able to make a decision if it need to render or not? so we just do the render without shallow check of the component ?

Sorry i don't understand what that second tries to deliver. I do see in the source code, props are not even shallow compared, it's just compared with a ===.

davidroffe commented 3 years ago

Thank you for the article. I do have a question.

Does the section "3.1 Useless props comparison" mean that even with the props comparison, it wouldn't be able to make a decision if it need to render or not? so we just do the render without shallow check of the component ?

Sorry i don't understand what that second tries to deliver. I do see in the source code, props are not even shallow compared, it's just compared with a ===.

I believe the general point is if you're repeatedly passing differing props frequently enough, then the component is going to rerender regardless, so there's no point in memoizing.

windmaomao commented 3 years ago

I believe the general point is if you're repeatedly passing differing props frequently enough, then the component is going to rerender regardless, so there's no point in memoizing.

thanks Dave, I thought all props are not compared, maybe only the reconciliation does in the end? So why do we care if the props are different, because React seems to take a different object of props in every render anyway?

I did a simple test

  App
     Branch1
       Count(count)
       Nothing1
         Nothing1-1
       Nothing2  
     Branch2

The count state is installed inside Branch1. It seems to me no matter what Nothing1-1 is rendered every time when this state changes.

Of course this is when no memo is used. But i'm still very surprised, because all the discussion on the internet about prop checking to me is just none sense now.

panzerdp commented 3 years ago

I believe the general point is if you're repeatedly passing differing props frequently enough, then the component is going to rerender regardless, so there's no point in memoizing.

That's the idea.

junho0956 commented 2 years ago

Thanks for sharing your knows!!!!!!!

panzerdp commented 2 years ago

Thanks for sharing your knows!!!!!!!

You're welcome @junho0956!

IAmMarcellus commented 2 years ago

Comment written by Robert Cooper on 07/20/2019 06:59:10

Great read! Where did the following quote come from?

Performance-related changes applied incorrectly can even harm performance. Use React.memo() wisely.

I'm curious to read more about performance implications of over using memoization in React applications.

@panzerdp Where did that quote come from? It doesn't look like there was ever a response and I'd like to read the article this came from too!

kenanyildiz commented 2 years ago

I think there is an escape for functions causes re-renders even provided username is same.

From

function Logout({ username, onLogout }) {
  return (
    <div onClick={onLogout}>
      Logout {username}
    </div>
  );
}
const MemoizedLogout = React.memo(Logout);

To: Here as you can see, I've provided a comparison function and it only compares username. I think this is good idea to memoize. IF you don't pass REALLY different onLogout fn as a prop in parent cmp any time, so you don't need to compare it..

function Logout({ username, onLogout }) {
  return (
    <div onClick={onLogout}>
      Logout {username}
    </div>
  );
}
const MemoizedLogout = React.memo(Logout, (prev, next) => prev.username === next.username);

What do you think?

decpk commented 2 years ago

Components using hooks can be freely wrapped in React.memo() to achieve memoization.

React always re-renders the component if the state changes, even if the component is wrapped in React.memo().

Does this mean that If we are wrapping a functional component with React.memo then there is no performance benefits. Have I understood something wrong? Please clear my doubt...

TheNemus commented 2 years ago

Hi, I'm trying to understand memo api but I still don't get it. I write a question in stackoverflow based on your post, can you give me a hand? Ty. https://stackoverflow.com/questions/72775948/how-react-memo-works-with-usecallback

ganeshshetty195 commented 2 years ago

Can you make a blog on how exactly react works?

tif-calin commented 1 year ago

@decpk

Does this mean that If we are wrapping a functional component with React.memo then there is no performance benefits. Have I understood something wrong? Please clear my doubt...

No. React will always rerender if state changes. The thing is that it will then also try to rerender all of the children of that component as well. React.memo allows us to make sure that BigExpensiveChild isn't rerendered if it "doesn't need to" (i.e. if the props we're passing to it are the same as they were last time it rendered).

However if BigExpensiveChild has some local state with useState and something triggers a change in that state, BigExpensiveChild will rerender. Which is what we want ofc

tif-calin commented 1 year ago

@kenanyildiz

Sure that works but that's exactly what the purpose of useCallback is meant to solve. In the parent component we can wrap the onLogout with useCallback and we won't have to worry about it triggering unnecessary rerenders

The reason why this is preferred is because it's more maintainable. What if in the future you also add a disabled prop to Logout. Then you would have to update your custom memoization function to check for differences in that as well.

Or what if in the future you wanted to expand the logic of onLogout to depend on some parent-level state. Then you wouldn't be able to rerender the Logout component when your function does actually change

Harry-Ross commented 9 months ago

Thanks for this - a really useful guide on how to use React memoization correctly 🙂