Fix crash with js-based sticky headers

Summary:
We're seeing ` inputRange must be monotonically increasing -1,0,0,-33,-32 ` which happens when we
have zero height headers, wherever those come from...maybe rendering null?

The math was also off and didn't handle variable height headers correctly, and it was confusing
because it was `setNextHeaderY` with the header y _minus it's height_, which only works
if the prev height was also the same height.

Reviewed By: furdei

Differential Revision: D4649404

fbshipit-source-id: c2c2d438fa0d0b979c2cbdfa5752eaf86c14768b
This commit is contained in:
Spencer Ahrens 2017-03-03 20:00:30 -08:00 committed by Facebook Github Bot
parent f6aad8b995
commit 94a333a2ea
2 changed files with 11 additions and 10 deletions

View File

@ -512,9 +512,7 @@ const ScrollView = React.createClass({
];
if (previousHeaderIndex != null) {
const previousHeader = this._stickyHeaderRefs.get(previousHeaderIndex);
previousHeader && previousHeader.setNextHeaderY(
event.nativeEvent.layout.y - event.nativeEvent.layout.height,
);
previousHeader && previousHeader.setNextHeaderY(event.nativeEvent.layout.y);
}
},

View File

@ -26,6 +26,7 @@ class ScrollViewStickyHeader extends React.Component {
state = {
measured: false,
layoutY: 0,
layoutHeight: 0,
nextHeaderLayoutY: (null: ?number),
};
@ -37,13 +38,14 @@ class ScrollViewStickyHeader extends React.Component {
this.setState({
measured: true,
layoutY: event.nativeEvent.layout.y,
layoutHeight: event.nativeEvent.layout.height,
});
this.props.onLayout(event);
};
render() {
const {measured, layoutY, nextHeaderLayoutY} = this.state;
const {measured, layoutHeight, layoutY, nextHeaderLayoutY} = this.state;
let translateY;
if (measured) {
@ -51,17 +53,18 @@ class ScrollViewStickyHeader extends React.Component {
// - Negative scroll: no translation
// - From 0 to the y of the header: no translation. This will cause the header
// to scroll normally until it reaches the top of the scroll view.
// - From the header y to the next header y: translate equally to scroll.
// This will cause the header to stay at the top of the scroll view.
// - Past the the next header y: no more translation. This will cause the header
// to continue scrolling up and make room for the next sticky header.
// - From header y to when the next header y hits the bottom edge of the header: translate
// equally to scroll. This will cause the header to stay at the top of the scroll view.
// - Past the collision with the next header y: no more translation. This will cause the
// header to continue scrolling up and make room for the next sticky header.
// In the case that there is no next header just translate equally to
// scroll indefinetly.
const inputRange = [-1, 0, layoutY];
const outputRange: Array<number> = [0, 0, 0];
if (nextHeaderLayoutY != null) {
inputRange.push(nextHeaderLayoutY, nextHeaderLayoutY + 1);
outputRange.push(nextHeaderLayoutY - layoutY, nextHeaderLayoutY - layoutY);
const collisionPoint = nextHeaderLayoutY - layoutHeight;
inputRange.push(collisionPoint, collisionPoint + 1);
outputRange.push(collisionPoint - layoutY, collisionPoint - layoutY);
} else {
inputRange.push(layoutY + 1);
outputRange.push(1);