Cancel list scroll reset after mouse move on wheel scroll (#9233)

- Use object properties rather than component state for
  mouseMovedRecently and scrollToTopOnMouseIdle flags

- Remove redundant scrollToTop prop call, also fixing an attempt to call
  an undefined prop.

Fixes #9217
This commit is contained in:
Les Orchard 2018-11-08 12:45:20 -05:00 committed by Eugen Rochko
parent b22db8162a
commit 9cfd610484

View File

@ -39,8 +39,6 @@ export default class ScrollableList extends PureComponent {
state = { state = {
fullscreen: null, fullscreen: null,
mouseMovedRecently: false,
scrollToTopOnMouseIdle: false,
}; };
intersectionObserverWrapper = new IntersectionObserverWrapper(); intersectionObserverWrapper = new IntersectionObserverWrapper();
@ -65,6 +63,8 @@ export default class ScrollableList extends PureComponent {
}); });
mouseIdleTimer = null; mouseIdleTimer = null;
mouseMovedRecently = false;
scrollToTopOnMouseIdle = false;
clearMouseIdleTimer = () => { clearMouseIdleTimer = () => {
if (this.mouseIdleTimer === null) { if (this.mouseIdleTimer === null) {
@ -80,29 +80,26 @@ export default class ScrollableList extends PureComponent {
this.mouseIdleTimer = this.mouseIdleTimer =
setTimeout(this.handleMouseIdle, MOUSE_IDLE_DELAY); setTimeout(this.handleMouseIdle, MOUSE_IDLE_DELAY);
this.setState(({ if (!this.mouseMovedRecently && this.node.scrollTop === 0) {
mouseMovedRecently, // Only set if we just started moving and are scrolled to the top.
scrollToTopOnMouseIdle, this.scrollToTopOnMouseIdle = true;
}) => ({ }
mouseMovedRecently: true, // Save setting this flag for last, so we can do the comparison above.
// Only set scrollToTopOnMouseIdle if we just started moving and were this.mouseMovedRecently = true;
// scrolled to the top. Otherwise, just retain the previous state.
scrollToTopOnMouseIdle:
mouseMovedRecently
? scrollToTopOnMouseIdle
: (this.node.scrollTop === 0),
}));
}, MOUSE_IDLE_DELAY / 2); }, MOUSE_IDLE_DELAY / 2);
handleWheel = throttle(() => {
this.scrollToTopOnMouseIdle = false;
}, 150, {
trailing: true,
});
handleMouseIdle = () => { handleMouseIdle = () => {
if (this.state.scrollToTopOnMouseIdle) { if (this.scrollToTopOnMouseIdle) {
this.node.scrollTop = 0; this.node.scrollTop = 0;
this.props.onScrollToTop();
} }
this.setState({ this.mouseMovedRecently = false;
mouseMovedRecently: false, this.scrollToTopOnMouseIdle = false;
scrollToTopOnMouseIdle: false,
});
} }
componentDidMount () { componentDidMount () {
@ -118,7 +115,7 @@ export default class ScrollableList extends PureComponent {
const someItemInserted = React.Children.count(prevProps.children) > 0 && const someItemInserted = React.Children.count(prevProps.children) > 0 &&
React.Children.count(prevProps.children) < React.Children.count(this.props.children) && React.Children.count(prevProps.children) < React.Children.count(this.props.children) &&
this.getFirstChildKey(prevProps) !== this.getFirstChildKey(this.props); this.getFirstChildKey(prevProps) !== this.getFirstChildKey(this.props);
if ((someItemInserted && this.node.scrollTop > 0) || this.state.mouseMovedRecently) { if ((someItemInserted && this.node.scrollTop > 0) || this.mouseMovedRecently) {
return this.node.scrollHeight - this.node.scrollTop; return this.node.scrollHeight - this.node.scrollTop;
} else { } else {
return null; return null;
@ -161,10 +158,12 @@ export default class ScrollableList extends PureComponent {
attachScrollListener () { attachScrollListener () {
this.node.addEventListener('scroll', this.handleScroll); this.node.addEventListener('scroll', this.handleScroll);
this.node.addEventListener('wheel', this.handleWheel);
} }
detachScrollListener () { detachScrollListener () {
this.node.removeEventListener('scroll', this.handleScroll); this.node.removeEventListener('scroll', this.handleScroll);
this.node.removeEventListener('wheel', this.handleWheel);
} }
getFirstChildKey (props) { getFirstChildKey (props) {