-
Notifications
You must be signed in to change notification settings - Fork 34
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: progressive dashboard loading (#244)
This restricts the loading of dashboard items to the current viewport, with a buffer of one half-viewport above and below. As such, only 1.5x the viewport is initially loaded. Other items will be loaded when the user scrolls them into this buffered viewport. * feat: progressive dashboard loading * feat: support initial buffer factor * feat: support progressive loading after a filter change * fix: use margin instead of padding to prevent overflow * Move memoize to its own file * chore: remove unused import
- Loading branch information
Showing
7 changed files
with
178 additions
and
39 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import memoizeOne from '../modules/memoizeOne'; | ||
|
||
describe('memoizeOne', () => { | ||
it('Should return the same value when called twice with shallow-equal arguments', () => { | ||
const object = { a: 0, b: 0 }; | ||
const fn = x => x.a + x.b; | ||
const memoizedFn = memoizeOne(fn); | ||
|
||
const val0 = memoizedFn(object); | ||
expect(val0).toBe(0); | ||
object.a = 1; // maintain shallow equality | ||
expect(fn(object)).toBe(1); | ||
const val1 = memoizedFn(object); | ||
expect(val1).toBe(0); | ||
}); | ||
|
||
it('Should forget the first value when called thrice', () => { | ||
const object = { a: 0, b: 0 }; | ||
const memoizedFn = memoizeOne(x => x.a + x.b); | ||
|
||
const val0 = memoizedFn(object); | ||
expect(val0).toBe(0); | ||
object.a = 1; // maintain shallow equality | ||
const val1 = memoizedFn({ a: 42, b: 84 }); // This will bump val0 out of the cache | ||
expect(val1).toBe(42 + 84); | ||
const val2 = memoizedFn(object); | ||
expect(val2).toBe(1); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
import React, { Component } from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import debounce from 'lodash/debounce'; | ||
|
||
const defaultDebounceMs = 100; | ||
const defaultBufferPx = 0; | ||
const defaultInitialBufferFactor = 0.5; | ||
|
||
class ProgressiveLoadingContainer extends Component { | ||
static propTypes = { | ||
children: PropTypes.node.isRequired, | ||
debounceMs: PropTypes.number, | ||
bufferPx: PropTypes.number, | ||
initialBufferFactor: PropTypes.number, | ||
}; | ||
static defaultProps = { | ||
debounceMs: defaultDebounceMs, | ||
bufferPx: defaultBufferPx, | ||
initialBufferFactor: defaultInitialBufferFactor, | ||
}; | ||
|
||
state = { | ||
shouldLoad: false, | ||
}; | ||
containerRef = null; | ||
shouldLoadHandler = null; | ||
|
||
checkShouldLoad(customBufferPx) { | ||
const bufferPx = customBufferPx || this.props.bufferPx; | ||
|
||
if (!this.containerRef) { | ||
return; | ||
} | ||
|
||
const rect = this.containerRef.getBoundingClientRect(); | ||
if ( | ||
rect.bottom > -bufferPx && | ||
rect.top < window.innerHeight + bufferPx | ||
) { | ||
this.setState({ | ||
shouldLoad: true, | ||
}); | ||
|
||
this.removeHandler(); | ||
} | ||
} | ||
|
||
registerHandler() { | ||
this.shouldLoadHandler = debounce( | ||
() => this.checkShouldLoad(), | ||
this.props.debounceMs | ||
); | ||
|
||
window.addEventListener('scroll', this.shouldLoadHandler); | ||
} | ||
removeHandler() { | ||
window.removeEventListener('scroll', this.shouldLoadHandler); | ||
} | ||
|
||
componentDidMount() { | ||
this.registerHandler(); | ||
|
||
const initialBufferPx = this.props.initialBufferFactor | ||
? this.props.initialBufferFactor * window.innerHeight | ||
: undefined; | ||
this.checkShouldLoad(initialBufferPx); | ||
} | ||
|
||
componentWillUnmount() { | ||
this.removeHandler(); | ||
} | ||
|
||
render() { | ||
const { | ||
children, | ||
debounceMs, | ||
bufferPx, | ||
initialBufferFactor, | ||
...props | ||
} = this.props; | ||
const { shouldLoad } = this.state; | ||
|
||
return ( | ||
<div ref={ref => (this.containerRef = ref)} {...props}> | ||
{shouldLoad && children} | ||
</div> | ||
); | ||
} | ||
} | ||
|
||
export default ProgressiveLoadingContainer; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
// Note that this ignores discrepancies in 'this', so shouldn't be used with bound functions | ||
// This is useful instead of lodash/memoize when we only need to memoize a single value | ||
// Inspiration: https://github.com/alexreardon/memoize-one | ||
|
||
const memoizeOne = fn => { | ||
let lastArgs = undefined; | ||
let lastValue = undefined; | ||
|
||
return (...args) => { | ||
if ( | ||
lastArgs && | ||
args.length === lastArgs.length && | ||
args.every((arg, i) => arg === lastArgs[i]) | ||
) { | ||
return lastValue; | ||
} | ||
lastArgs = args; | ||
lastValue = fn(...args); | ||
return lastValue; | ||
}; | ||
}; | ||
|
||
export default memoizeOne; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters