Skip to content

Commit

Permalink
VirtualizedList optimization - memoize FlatList._renderer
Browse files Browse the repository at this point in the history
Summary:
Problem:
All CellRenderers rerender every time the containing VirtualizedList is rerendered. This is due to the following:
- Lambda is created for each CellRenderer's onLayout prop on every VirtualizedList render (fixed in D35061321 (19cf702))
- CellRenderer's parentProps prop changes on every VirtualizedList render (fixed in D35062323 (adb2962))
- FlatList recreates renderItem/ListItemComponent in FlatList._renderer (addressed in this diff)

Changelog:
[Internal] - VirtualizedList optimization - memoize FlatList._renderer

Reviewed By: ryancat

Differential Revision: D35067472

fbshipit-source-id: 124629d94821f35b8943730839fbe72f547e80fd
  • Loading branch information
genkikondo authored and facebook-github-bot committed Mar 24, 2022
1 parent adb2962 commit c231d5e
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 6 deletions.
1 change: 1 addition & 0 deletions BUCK
Original file line number Diff line number Diff line change
Expand Up @@ -727,6 +727,7 @@ rn_library(
"//xplat/js:node_modules__base64_19js",
"//xplat/js:node_modules__event_19target_19shim",
"//xplat/js:node_modules__invariant",
"//xplat/js:node_modules__memoize_19one",
"//xplat/js:node_modules__nullthrows",
"//xplat/js:node_modules__pretty_19format",
"//xplat/js:node_modules__promise",
Expand Down
33 changes: 27 additions & 6 deletions Libraries/Lists/FlatList.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import type {
} from './ViewabilityHelper';
import type {RenderItemType, RenderItemProps} from './VirtualizedList';
import {keyExtractor as defaultKeyExtractor} from './VirtualizeUtils';
import memoizeOne from 'memoize-one';

type RequiredProps<ItemT> = {|
/**
Expand Down Expand Up @@ -141,6 +142,10 @@ type OptionalProps<ItemT> = {|
* See `ScrollView` for flow type and further documentation.
*/
fadingEdgeLength?: ?number,
/**
* Enable an optimization to memoize the item renderer to prevent unnecessary rerenders.
*/
strictMode?: boolean,
|};

/**
Expand Down Expand Up @@ -578,9 +583,14 @@ class FlatList<ItemT> extends React.PureComponent<Props<ItemT>, void> {
};
}

_renderer = () => {
const {ListItemComponent, renderItem, columnWrapperStyle} = this.props;
const numColumns = numColumnsOrDefault(this.props.numColumns);
_renderer = (
ListItemComponent: ?(React.ComponentType<any> | React.Element<any>),
renderItem: ?RenderItemType<ItemT>,
columnWrapperStyle: ?ViewStyleProp,
numColumns: ?number,
extraData: ?any,
) => {
const cols = numColumnsOrDefault(numColumns);

let virtualizedListRenderKey = ListItemComponent
? 'ListItemComponent'
Expand All @@ -605,7 +615,7 @@ class FlatList<ItemT> extends React.PureComponent<Props<ItemT>, void> {
* This comment suppresses an error found when Flow v0.111 was deployed.
* To see the error, delete this comment and run Flow. */
[virtualizedListRenderKey]: (info: RenderItemProps<ItemT>) => {
if (numColumns > 1) {
if (cols > 1) {
const {item, index} = info;
invariant(
Array.isArray(item),
Expand All @@ -616,7 +626,7 @@ class FlatList<ItemT> extends React.PureComponent<Props<ItemT>, void> {
{item.map((it, kk) => {
const element = renderer({
item: it,
index: index * numColumns + kk,
index: index * cols + kk,
separators: info.separators,
});
return element != null ? (
Expand All @@ -632,14 +642,19 @@ class FlatList<ItemT> extends React.PureComponent<Props<ItemT>, void> {
};
};

_memoizedRenderer = memoizeOne(this._renderer);

render(): React.Node {
const {
numColumns,
columnWrapperStyle,
removeClippedSubviews: _removeClippedSubviews,
strictMode = false,
...restProps
} = this.props;

const renderer = strictMode ? this._memoizedRenderer : this._renderer;

return (
<VirtualizedList
{...restProps}
Expand All @@ -651,7 +666,13 @@ class FlatList<ItemT> extends React.PureComponent<Props<ItemT>, void> {
removeClippedSubviews={removeClippedSubviewsOrDefault(
_removeClippedSubviews,
)}
{...this._renderer()}
{...renderer(
this.props.ListItemComponent,
this.props.renderItem,
columnWrapperStyle,
numColumns,
this.props.extraData,
)}
/>
);
}
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@
"hermes-engine": "~0.11.0",
"invariant": "^2.2.4",
"jsc-android": "^250230.2.1",
"memoize-one": "^5.0.0",
"metro-react-native-babel-transformer": "0.69.1",
"metro-runtime": "0.69.1",
"metro-source-map": "0.69.1",
Expand Down
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4926,6 +4926,11 @@ map-visit@^1.0.0:
dependencies:
object-visit "^1.0.0"

memoize-one@^5.0.0:
version "5.2.1"
resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.2.1.tgz#8337aa3c4335581839ec01c3d594090cebe8f00e"
integrity sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==

merge-stream@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
Expand Down

0 comments on commit c231d5e

Please sign in to comment.