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:
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 usesReact.createElement
to create a new element from passed-inprops.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 memoizedtype
argument, according to its definition:
React.createElement(
type, <- If this one is memoized, I mean
[props],
[...children]
)
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:
and use it in render method like so:
createElement
doesn't handle memoization, it simply creates a react element. Memoization itself assures that the component will be not created if nothing changed.