From dfccc1f596d35b2ea33ca66ce33b815efe087dc3 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Mon, 3 Sep 2018 09:27:55 -0700 Subject: [PATCH] Added memoized-list-items sandbox --- .../sandboxes/memoized-list-items/index.js | 83 +++++++++++++++++++ .../memoized-list-items/package.json | 10 +++ .../sandboxes/memoized-list-items/styles.css | 19 +++++ website/src/code/MemoizedListItems.js | 19 ++--- .../examples/MemoizedListItemsExample.js | 22 ++--- 5 files changed, 130 insertions(+), 23 deletions(-) create mode 100644 website/sandboxes/memoized-list-items/index.js create mode 100644 website/sandboxes/memoized-list-items/package.json create mode 100644 website/sandboxes/memoized-list-items/styles.css diff --git a/website/sandboxes/memoized-list-items/index.js b/website/sandboxes/memoized-list-items/index.js new file mode 100644 index 0000000..3ba6912 --- /dev/null +++ b/website/sandboxes/memoized-list-items/index.js @@ -0,0 +1,83 @@ +import React, { PureComponent } from 'react'; +import ReactDOM from 'react-dom'; +import memoize from 'memoize-one'; +import { FixedSizeList as List } from 'react-window'; + +const generateItems = numItems => + Array(numItems) + .fill(true) + .map(_ => ({ + isActive: false, + label: Math.random() + .toString(36) + .substr(2), + })); + +// If list items are expensive to render, +// Consider using PureComponent to avoid unnecessary re-renders. +// https://reactjs.org/docs/react-api.html#reactpurecomponent +class ListItem extends PureComponent { + render() { + const { data, index, style } = this.props; + + // Data passed to List as "itemData" is available as props.data + const { items, toggleItemActive } = data; + const item = items[index]; + + return ( +
toggleItemActive(index)} style={style}> + {item.label} is {item.isActive ? 'active' : 'inactive'} +
+ ); + } +} + +// This helper function memoizes incoming props, +// To avoid causing unnecessary re-renders pure ListItem components. +// This is only needed since we are passing multiple props with a wrapper object. +// If we were only passing a single, stable value (e.g. items), +// We could just pass the value directly. +const getItemData = memoize((items, toggleItemActive) => ({ + items, + toggleItemActive, +})); + +class App extends PureComponent { + state = { + items: generateItems(1000), + }; + + toggleItemActive = index => + this.setState(prevState => { + const item = prevState.items[index]; + const items = prevState.items.concat(); + items[index] = { + ...item, + isActive: !item.isActive, + }; + return { items }; + }); + + render() { + const { items } = this.state; + + // Bundle additional data to list items using the "itemData" prop. + // It will be accessible to item renderers as props.data. + // Memoize this data to avoid bypassing shouldComponentUpdate(). + const itemData = getItemData(items, this.toggleItemActive); + + return ( + + {ListItem} + + ); + } +} + +ReactDOM.render(, document.getElementById('root')); diff --git a/website/sandboxes/memoized-list-items/package.json b/website/sandboxes/memoized-list-items/package.json new file mode 100644 index 0000000..82e41d1 --- /dev/null +++ b/website/sandboxes/memoized-list-items/package.json @@ -0,0 +1,10 @@ +{ + "description": "Demo of react-window with advanced memoization techniques", + "main": "src/index.js", + "dependencies": { + "memoize-one": "^4", + "react": "^16", + "react-dom": "^16", + "react-window": "^1" + } +} diff --git a/website/sandboxes/memoized-list-items/styles.css b/website/sandboxes/memoized-list-items/styles.css new file mode 100644 index 0000000..5d3455d --- /dev/null +++ b/website/sandboxes/memoized-list-items/styles.css @@ -0,0 +1,19 @@ +html { + font-family: sans-serif; + font-size: 12px; +} + +.List { + border: 1px solid #d9dddd; +} + +.ListItemEven, +.ListItemOdd { + display: flex; + align-items: center; + justify-content: center; +} + +.ListItemEven { + background-color: #f8f8f0; +} diff --git a/website/src/code/MemoizedListItems.js b/website/src/code/MemoizedListItems.js index 4564696..7a0dcaa 100644 --- a/website/src/code/MemoizedListItems.js +++ b/website/src/code/MemoizedListItems.js @@ -44,27 +44,26 @@ class Example extends PureComponent { toggleItemActive = index => this.setState(prevState => { const item = prevState.items[index]; - return { - items: { - ...prevState.items, - [index]: { - ...item, - isActive: !item.isActive, - }, - }, + const items = prevState.items.concat(); + items[index] = { + ...item, + isActive: !item.isActive, }; + return {items}; }); render() { + const { items } = this.state; + // Bundle additional data to list items using the "itemData" prop. // It will be accessible to item renderers as props.data. // Memoize this data to avoid bypassing shouldComponentUpdate(). - const itemData = getItemData(this.state.items, this.toggleItemActive); + const itemData = getItemData(items, this.toggleItemActive); return ( - Array(1000) +const generateItems = numItems => + Array(numItems) .fill(true) .map(_ => ({ isActive: false, @@ -49,22 +49,18 @@ class ItemRenderer extends PureComponent { export default class MemoizedListItemsExample extends PureComponent { state = { - items: generateItems(), + items: generateItems(1000), }; toggleItemActive = index => this.setState(prevState => { const item = prevState.items[index]; - - return { - items: { - ...prevState.items, - [index]: { - ...item, - isActive: !item.isActive, - }, - }, + const items = prevState.items.concat(); + items[index] = { + ...item, + isActive: !item.isActive, }; + return { items }; }); render() { @@ -82,7 +78,7 @@ export default class MemoizedListItemsExample extends PureComponent {