Throttle updates to assets on sprite selector item via HOC
Adds unit tests for this HOC.
This commit is contained in:
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,
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user