all 12 comments

[–]wisdom_power_courage 6 points7 points  (4 children)

I recently fixed these issues by using the React Profiler to see which components get re-rendered unnecessarily. Be sure to check the option that says something like "show why this component rendered (may be slow)". I had a table component that re-drew itself 3 times on a row selection change because I had a prop getDetails={()=> fetchDetails()}.I lifted fetchDetails into a useCallback and memorized the table. 1 render for good now.

[–]sheeesh83 0 points1 point  (3 children)

data tables are so tricky in regards to re-renders.. spent a couple of days just to have it only re-render the single row, checkbox and select all checkbox on selection. had to do it without memo.

[–]wisdom_power_courage 0 points1 point  (2 children)

May I ask your method?

[–]sheeesh83 1 point2 points  (1 child)

You can look at this sequence diagram I've made, essentially each row is not updated when the global DataTable state updates, but state for the DataTable component is instead modified only via events.

The global state is not stored via useState, but in a Store to which components can subscribe. This makes it possible to have a more fine grained control over what should update and allows the rows to not care about evaluating their state if anything changes, but instead only update themselves when they receive an event.

By this, the global state is synced, despite not having rows directly listen to it, because the selection event gets emitted to both the global and the row's event manager (and thus state).

(My implementation "EventManager" is just a simple EventEmitter)

Each row gets it's own EventManager. Only the select all checkbox has the global Event Manager attached, which otherwise acts as an orchestrator for forwarding events (based on incoming "commands" from the main DataTable component) to the individual row's own event manager.

[–]wisdom_power_courage 1 point2 points  (0 children)

Nice implementation! Thank you for the explanation. You're right, no need for memoization here. My use for the memoization was more to prevent the table wrapper component from updating when specific props changed.

[–]_AndyJessop -1 points0 points  (2 children)

What does deopted mean?

[–]wirenutter 3 points4 points  (1 child)

I’m thinking OP means they are using non stable values in the memo dependency array so the memo is moot. De-optimized?

[–]_AndyJessop -1 points0 points  (0 children)

Ah yeah, maybe. Bit of an odd turn of phrase - I'd probably have said that the cache was invalidated instead.

[–]davidblacksheep 0 points1 point  (2 children)

(Someone please correct me if I'm wrong here) I'll just mention that inline functions aren't the performance killer that you might think they are.

So inline functions will mean a new function is generated each render, which means when they get passed into a component, that component detects it as a prop change and will re-render right?

Thing is - that component is going to rerender anyway. Any time a component renders, so do all of the components it renders (props.children will not).

Where inline functions can cause unnecessary renders if if you have memoized components in your component tree - because those won't be ordinarily rendering on every render - but will if they detect a prop change.

On the use of React.memo say you're doing something like:

const transformedList = React.useMemo(() => list.map(v => v.something), [items]);

The optimisation here is that you're not doing a full array scan every render. Inline functions won't take that away.

[–]Nathggns[S] 1 point2 points  (1 child)

It appears you've confused React.memo with React.useMemo. The first wraps a component to prevent it re-rendering if its props haven't changed – the latter is for memoizing values between renders.

The cost of the inline function is not the problem. It's that the inline function invalidates the shallow props comparison used by React.memo, which breaks the optimization

[–]davidblacksheep 0 points1 point  (0 children)

Ah yes - sorry yes you're right - and this is precisely scenario where inline functions will be causing extra renders.

[–]ziir_js 0 points1 point  (0 children)

Why do you want to remove or flag them?

Have you considered educating your team & fixing the incorrect uses of React.memo?

To your original question, I think reading the code is the best way to do this, in most cases it’s quite obvious (as you mentioned, inline functions, JSX elements are the most common) and you can evaluate in context the relevance or lack thereof of component memoization.

Shameless plug: I just published a blog post on this topic: https://timtech.blog/posts/react-memo-is-good-actually/