'How to prevent rerendering of children inside List component of react-window

Problem

I'm trying to achieve a behaviour, when merely changed items of the list are being updated and rerendered with new data.

I wrote a component which is being updated when timer is off:

import { FixedSizeList as List } from 'react-window';


function ProfilesList() {
   ...

   // Here is an effect which fetches new data every time and component is rerendered with new data

   React.useEffect(() => {
        const timer = setInterval(() => {
            dispatch(loadProfilesList());
        }, intervalTime);
        return () => {
            clearInterval(timer);
        };
    }, [dispatch]);

    ...

    return (
       <List height={758} width={625} itemSize={82} itemCount={filteredProfiles.length}>
            {({ index, style }: { index: number; style: any }): React.ReactElement => {
                return (
                    <div style={style} key={index}>
                        <Tooltip title={t`stop` as string}>
                            <Button>lalla</Button>
                        </Tooltip>
                    </div>
                );
            }}
        </List>
    );

And as a result, when timer is off, all items are rerendered, but none of them wasn't changed practically, which is resulting in flickering appearance of a tooltip:

flickering tooltip

Some investigation

I checked react-window FixedSizeList (which is used for List) render method. And here is some questions which I didn't answer myself yet:

  • Why list itself is rerendered if all props are the same? Seems like this isn't optimized, so the component will be rerendered at any case, won't it? (render method sources)

  • And looking into render method, how props.children are used, List component uses React.createElement to create a new element from passed-in props.children. So I started to think that it's possible to memoize children and came up with this change to return result from function component:

    const memoizedChild = React.useMemo(() => {
        const memo = ({ index, style }: { index: number; style: any }): React.ReactElement => {
            return (
                <div style={style} key={index}>
                    <Tooltip title={t`stop` as string}>
                        <Button>lalla</Button>
                    </Tooltip>
                </div>
            );
        };
        memo.displayName = 'memo';
        return memo;
    }, []);

    return (
        <List height={758} width={625} itemSize={82} itemCount={filteredProfiles.length}>
            {memoizedChild}
        </List>
    );

And it looks like React.createElement doesn't give a damn about memoized children component and therefore React mounts it over and over again. So I stumbled upon this, wondering if it's React.createElement to blame or user code (my code) is written badly.

Questions

  • Is it possible to resolve this tooltip flickering appearance issue with user code changes? What should be changed?

These will be also helpful to have answered:

  • If previous answer is no, then, what changes should be made inside react-window?

  • And this one especial curious, does React.createElement handles properly memoized type argument, according to its definition:

React.createElement(
  type, <- If this one is memoized, I mean
  [props],
  [...children]
)


Solution 1:[1]

Is it possible to resolve this tooltip flickering appearance issue with user code changes? What should be changed?

All is pretty straight. When I was passing a function as a child, it was constantly being created over and over again. So the solution was to move child function definition out of render method like so:

const MyButton: React.FC<{ style: any; index: number }> = ({ index, style }) => {
    return (
        <div style={style} key={index}>
            <Tooltip title={t`stop` as string}>
                <Button>lalla</Button>
            </Tooltip>
        </div>
    );
};

and use it in render method like so:

<List height={758} width={625} itemSize={82} itemCount={filteredProfiles.length}>
    {MyButton}
</List>

And this one especial curious, does React.createElement handles properly memoized type argument, according to its definition ...

createElement doesn't handle memoization, it simply creates a react element. Memoization itself assures that the component will be not created if nothing changed.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Juriy