diff --git a/routes/_components/virtualList/virtualListStore.js b/routes/_components/virtualList/virtualListStore.js index 61c40688..b8772bb1 100644 --- a/routes/_components/virtualList/virtualListStore.js +++ b/routes/_components/virtualList/virtualListStore.js @@ -1,5 +1,6 @@ import { mark, stop } from '../../_utils/marks' import { RealmStore } from '../../_utils/RealmStore' +import { reselect } from '../../_utils/reselect' const VIEWPORT_RENDER_FACTOR = 3 @@ -21,9 +22,10 @@ virtualListStore.computeForRealm('scrollHeight', 0) virtualListStore.computeForRealm('offsetHeight', 0) virtualListStore.computeForRealm('itemHeights', {}) -virtualListStore.compute('visibleItems', +virtualListStore.compute('rawVisibleItems', ['items', 'scrollTop', 'itemHeights', 'offsetHeight', 'showHeader', 'headerHeight'], (items, scrollTop, itemHeights, offsetHeight, showHeader, headerHeight) => { + window.rawVisibleItemsComputed = (window.rawVisibleItemsComputed || 0) + 1 mark('compute visibleItems') if (!items) { return null @@ -58,6 +60,8 @@ virtualListStore.compute('visibleItems', return visibleItems }) +reselect(virtualListStore, 'visibleItems', 'rawVisibleItems') + virtualListStore.compute('heightWithoutFooter', ['items', 'itemHeights', 'showHeader', 'headerHeight'], (items, itemHeights, showHeader, headerHeight) => { @@ -97,6 +101,14 @@ virtualListStore.compute('allVisibleItemsHaveHeight', if (process.browser && process.env.NODE_ENV !== 'production') { window.virtualListStore = virtualListStore + + virtualListStore.observe('visibleItems', () => { + window.visibleItemsChangedCount = (window.visibleItemsChangedCount || 0) + 1 + }) + + virtualListStore.observe('rawVisibleItems', () => { + window.rawVisibleItemsChangedCount = (window.rawVisibleItemsChangedCount || 0) + 1 + }) } export { diff --git a/routes/_utils/reselect.js b/routes/_utils/reselect.js new file mode 100644 index 00000000..556e6306 --- /dev/null +++ b/routes/_utils/reselect.js @@ -0,0 +1,38 @@ +// Avoid re-renders by caching the most recent value of an array +// or an object, using an approach similar to https://github.com/reactjs/reselect. +// This avoids the issue where Svelte may keep re-rendering because it doesn't +// know if an object/array has changed or not. + +import isEqual from 'lodash/isEqual' + +if (process.browser && process.env.NODE_ENV !== 'production') { + window.reselectStats = {} +} + +export function reselect (store, outputKey, inputKey) { + let prevValue + let nextValue + let count = 0 + let countKey = `${outputKey}_reselectCount` + + store.compute(countKey, [inputKey], input => { + if (process.browser && process.env.NODE_ENV !== 'production') { + window.reselectStats[inputKey] = window.reselectStats[inputKey] || {numInputChanges: 0, numOutputChanges: 0} + window.reselectStats[inputKey].numInputChanges++ + } + if (!isEqual(prevValue, input)) { + nextValue = input + count++ + } + return count + }) + + store.compute(outputKey, [countKey], () => { + if (process.browser && process.env.NODE_ENV !== 'production') { + window.reselectStats[inputKey].numOutputChanges++ + } + prevValue = nextValue + nextValue = null + return prevValue + }) +}