Throttle updates to assets on sprite selector item via HOC

Adds unit tests for this HOC.
This commit is contained in:
Paul Kaplan committed 2019-01-23 14:30:41 -05:00
1 parent 0910b2ece1
commit 0bc6de1362
3 files changed
+101 -1

No files matched your search

@@ -8,9 +8,12 @@ import Box from '../box/box.jsx';
import SpriteSelectorItem from '../../containers/sprite-selector-item.jsx';
import SortableHOC from '../../lib/sortable-hoc.jsx';
import SortableAsset from '../asset-panel/sortable-asset.jsx';
import ThrottledPropertyHOC from '../../lib/throttled-property-hoc.jsx';
import styles from './sprite-selector.css';
const ThrottledSpriteSelectorItem = ThrottledPropertyHOC('asset', 500, SpriteSelectorItem);
const SpriteList = function (props) {
const {
containerRef,
@@ -74,7 +77,7 @@ const SpriteList = function (props) {
onAddSortable={onAddSortable}
onRemoveSortable={onRemoveSortable}
>
<SpriteSelectorItem
<ThrottledSpriteSelectorItem
asset={sprite.costume && sprite.costume.asset}
className={classNames(styles.sprite, {
[styles.raised]: isRaised,
+43
View File
@@ -0,0 +1,43 @@
import React from 'react';
/* Higher Order Component to throttle updates to specific props.
* Why? Because certain prop updates are expensive, and need to be throttled.
* This allows renders when other properties change, and will use the last
* rendered value of a prop for comparison.
* @param {string} propName the name of the prop to throttle updates from.
* @param {string} throttleTime the minimum time between updates to that specific property.
* @param {React.Component} WrappedComponent component who will not update for certain props.
* @returns {React.Component} component with throttling behavior
*/
const ThrottledPropertyHOC = function (propName, throttleTime, WrappedComponent) {
class ThrottledPropertyWrapper extends React.Component {
shouldComponentUpdate (nextProps) {
for (const property in nextProps) {
if (property !== propName && this.props[property] !== nextProps[property]) {
return true; // Always update if another property has changed
}
}
// If only that prop has changed, allow update to go to render based
// on _lastRenderedTime and _lastRenderTime are updated in render
if (nextProps[propName] !== this._lastRenderedValue &&
Date.now() - this._lastRenderTime > throttleTime
) {
return true; // Allow this update to go to render
}
return false;
}
render () {
this._lastRenderTime = Date.now();
this._lastRenderedValue = this.props[propName];
return (
<WrappedComponent {...this.props} />
);
}
}
return ThrottledPropertyWrapper;
};
export default ThrottledPropertyHOC;
@@ -0,0 +1,54 @@
import React from 'react';
import {mount} from 'enzyme';
import ThrottledPropertyHOC from '../../../src/lib/throttled-property-hoc.jsx';
describe('VMListenerHOC', () => {
let mounted;
const throttleTime = 500;
beforeEach(() => {
const Component = ({propToThrottle, doNotThrottle}) => (
<input
name={doNotThrottle}
value={propToThrottle}
/>
);
const WrappedComponent = ThrottledPropertyHOC('propToThrottle', throttleTime, Component);
global.Date.now = () => 0;
mounted = mount(
<WrappedComponent
doNotThrottle="oldvalue"
propToThrottle={0}
/>
);
});
test('it passes the props on initial render ', () => {
expect(mounted.find('[value=0]').exists()).toEqual(true);
expect(mounted.find('[name="oldvalue"]').exists()).toEqual(true);
});
test('it does not rerender if throttled prop is updated too soon', () => {
global.Date.now = () => throttleTime / 2;
mounted.setProps({propToThrottle: 1});
mounted.update();
expect(mounted.find('[value=0]').exists()).toEqual(true);
});
test('it does rerender if throttled prop is updated after throttle timeout', () => {
global.Date.now = () => throttleTime * 2;
mounted.setProps({propToThrottle: 1});
mounted.update();
expect(mounted.find('[value=1]').exists()).toEqual(true);
});
test('it does rerender if a non-throttled prop is changed', () => {
global.Date.now = () => throttleTime / 2;
mounted.setProps({doNotThrottle: 'newvalue', propToThrottle: 2});
mounted.update();
expect(mounted.find('[name="newvalue"]').exists()).toEqual(true);
expect(mounted.find('[value=2]').exists()).toEqual(true);
});
});