Skip to content

Commit 610f8c6

Browse files
committed
Fix bug where the list would scroll down by a small amount when adding items after scrolling up
1 parent 14dc5f9 commit 610f8c6

File tree

2 files changed

+23
-10
lines changed

2 files changed

+23
-10
lines changed

demo/src/App.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ class App extends React.Component {
1212
}
1313
}
1414
createItem(){
15-
return Math.random()
15+
return Math.random() * 10
1616
}
1717
createItems(n){
1818
const arr = []
@@ -26,6 +26,11 @@ class App extends React.Component {
2626
items: [this.createItem(), ...this.state.items]
2727
})
2828
}
29+
addItemsToStart(n){
30+
this.setState({
31+
items: [...this.createItems(n), ...this.state.items]
32+
})
33+
}
2934
addItemToEnd(){
3035
this.setState({
3136
items: this.state.items.concat(this.createItem())
@@ -50,6 +55,11 @@ class App extends React.Component {
5055
>
5156
add 1 to start
5257
</button>
58+
<button
59+
onClick={e => this.addItemsToStart(5)}
60+
>
61+
add 5 to start
62+
</button>
5363
<ScrolledList
5464
items={items}
5565
onScrolled={e => console.log('the list was scrolled!')}

src/index.js

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,20 @@ const isScrolledDown = (el, threshold) => {
88
return bottom >= el.scrollHeight - threshold
99
}
1010

11-
const isScrolledTop = el => el.scrollTop === 0
11+
const isScrolledUp = el => el.scrollTop === 0
1212

1313
const scrollDown = el => el.scrollTop = el.scrollHeight - el.clientHeight
1414

1515
const scrollDownBy = (amount, el) => el.scrollTop += amount
16+
const scrollUpBy = (amount, el) => el.scrollTop -= amount
1617

1718
export default (Component, { isScrolledDownThreshold = 150 } = { }) => class extends React.PureComponent {
1819
constructor(props){
1920
super(props)
2021
this._isScrolledDown = true /* whether the user has scrolled down */
2122
this._el = null
22-
this.scrollHeight = null
23+
this._scrollHeight = null
24+
this._isScrolledUp = null
2325
}
2426
scrollDownIfNeeded(){
2527
if(this._isScrolledDown && hasOverflow(this._el)){
@@ -28,7 +30,7 @@ export default (Component, { isScrolledDownThreshold = 150 } = { }) => class ext
2830
}
2931
handleScroll(e){
3032
this._isScrolledDown = isScrolledDown(this._el, isScrolledDownThreshold)
31-
if(isScrolledTop(this._el)){
33+
if(isScrolledUp(this._el)){
3234
this.props.onScrolledTop && this.props.onScrolledTop(e)
3335
}
3436
this.props.onScrolled && this.props.onScrolled(e)
@@ -37,17 +39,18 @@ export default (Component, { isScrolledDownThreshold = 150 } = { }) => class ext
3739
this.scrollDownIfNeeded()
3840
}
3941
componentWillUpdate(nextProps, nextState){
40-
this.scrollHeight = this._el.scrollHeight
42+
this._scrollHeight = this._el.scrollHeight
43+
this._isScrolledUp = isScrolledUp(this._el)
4144
}
42-
4345
componentDidUpdate(){
44-
if(this.scrollHeight !== null){
46+
/* if the list is scrolled all the way up and new items are added, preserve the current scroll position */
47+
if(this._isScrolledUp && this._scrollHeight !== null){
4548
/* the scroll height increased by this much during the update */
46-
const difference = this._el.scrollHeight - this.scrollHeight
47-
this.scrollHeight = null
49+
const difference = this._el.scrollHeight - this._scrollHeight
50+
this._scrollHeight = null
4851
scrollDownBy(difference, this._el)
4952
}
50-
this.scrollDownIfNeeded()
53+
else this.scrollDownIfNeeded()
5154
}
5255
render(){
5356
const { onScrolled, onScrolledTop, ...rest } = this.props

0 commit comments

Comments
 (0)