Reputation: 33
I'm building a view in a react native app with a section list component and it also has a header above which will show aggregate data about the list. The header view should shrink down with animation when you scroll. I've got all the animation working fine, but issues arise when adding in infinite scrolling using the onEndreached
prop. When new data is added or the state/props change, it jumps up to the top of the list.
To make the SectionList animate, I've wrapped it using Animated.createAnimatedComponent
to create a <AnimatedSectionList />
component. It's when it's wrapped with Animated that the issues arise.
In the below-linked gif you can see that it jumps to the top of the list when I scroll down. This is when the state changes and we start fetching more data. https://monosnap.com/file/2aH5iPF2XcygEz381bpHW9QGPGHA2Z
I've tried using getItemLayout
to calculate the list size hoping because I read that when react doesn't know the height of the list, it can be jumpy.
I've also tried scrolling to a section after new data is loaded using scrollTo
but that doesn't work either. I was doing this with componentDidUpdate
lifecycle method, but componentDidUpdate
wasn't being called consistently when new data came in.
Here's my code.
const DataItem = [
{ name: "test name", value: "testvalue" },
{ name: "test name", value: "testvalue" },
{ name: "test name", value: "testvalue" },
{ name: "test name", value: "testvalue" },
{ name: "test name", value: "testvalue" }
];
const initialSections = [
{ title: "MY DETAILS", data: DataItem },
{ title: "MY COMMUNITY", data: DataItem },
{ title: "MY FAMILY", data: DataItem }
];
const styles = {
item: {
flex: 1,
height: 50,
marginTop: 1,
backgroundColor: "#dddddd"
},
sectionHeader: {
flex: 1,
height: 20,
marginTop: 1,
backgroundColor: "#00ff00"
}
};
class Edit extends Component {
constructor(props) {
super(props);
this.state = {
scrollEnabled: true,
scrollY: new Animated.Value(0),
sections: initialSections,
refreshing: false
};
_renderSectionHeader(section) {
return (
<View style={styles.sectionHeader}>
<Text> {section.title} </Text>
</View>
);
}
_renderItem(item) {
return (
<View style={styles.item}>
<Text>{item.name}</Text>
</View>
);
}
handleStateButton() {
this.setState({ refreshing: true });
}
fetchMoreData() {
this.setState({ refreshing: true });
const newData = [...this.state.sections, ...initialSections];
setTimeout(
function() {
this.setState({ sections: newData, refreshing: false }); }.bind(this),
2000
);
}
render() {
const backgroundScrollY = this.state.scrollY.interpolate({
inputRange: [0, 224],
outputRange: [0, -50],
extrapolate: "clamp"
});
const backgroundScrollHeight = this.state.scrollY.interpolate({
inputRange: [0, 224],
outputRange: [1, 0.75],
extrapolate: "clamp"
});
const listScrollY = this.state.scrollY.interpolate({
inputRange: [0, 224],
outputRange: [0, -10],
extrapolate: "clamp"
});
const infoOpacity = this.state.scrollY.interpolate({
inputRange: [0, 0.5, 150],
outputRange: [1, 1, 0],
extrapolate: "clamp"
});
const AnimatedSectionList = Animated.createAnimatedComponent(SectionList);
return (
<View
style={{
flex: 1,
// alignItems: "flex-start",
flexDirection: "column"
}}
>
<Animated.View
style={[
{
position: "relative",
// flex: 1,
// alignSelf: "flex-start",
top: 0,
minHeight: 200,
height: 300,
backgroundColor: "#ddee99",
border: "1px solid #0000FF",
justifyContent: "center",
alignItems: "center"
},
{ transform: [{ scaleY: backgroundScrollHeight }] }
]}
>
<Animated.Image
source={{
uri:
"https://images.unsplash.com/photo-1558901591-3a5f333830dd?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1350&q=80"
}}
style={[
{ position: "absolute", backgroundColor: "#cccccc" },
{ transform: [{ translateY: backgroundScrollY }] }
]}
blurRadius={1.5}
/>
<Text>{this.state.refreshing && `Fetching data`}</Text>
<Button onPress={this.fetchMoreData} title="Get More Data" />
<Button onPress={this.handleStateButton} title="Changing State" />
</Animated.View>
<AnimatedSectionList
ref={ref => (this.sectionListRef = ref)}
bounces={false}
scrollEnabled={this.state.scrollEnabled}
style={[
{ position: "relative" },
{ transform: [{ translateY: listScrollY }] }
]}
onScroll={Animated.event(
[{ nativeEvent: { contentOffset: { y: this.state.scrollY } } }],
{ listener: this._onScroll.bind(this) }
)}
sections={this.state.sections}
renderSectionHeader={({ section }) =>
this._renderSectionHeader(section)
}
ListHeaderComponent={() => (
<View
style={{
flex: 1,
flexDirection: "row",
alignItems: "center",
height: 40,
backgroundColor: "#ff00ff"
}}
>
<Text>List Header</Text>
</View>
)}
ListFooterComponent={() => (
<View
style={{
flex: 1,
flexDirection: "row",
alignItems: "center",
height: 80,
backgroundColor: "#FF0"
}}
>
<Text>List Footer</Text>
</View>
)}
getItemLayout={this.getItemLayout}
renderItem={({ item }) => this._renderItem(item)}
keyExtractor={(item, index) => index}
stickySectionHeadersEnabled={true}
onEndReachedThreshold={0.75}
onEndReached={d => {
this.fetchMoreData();
}}
/>
</View>
);
}
}
I would expect that the list would remain at the current scrolled position when the state or props change, but instead, it jumps to the top, defeating the purpose of infinite scroll.
Would appreciate any thoughts on the best way to approach this issue.
Upvotes: 3
Views: 3386
Reputation: 46
I put the
const AnimatedSectionList = Animated.createAnimatedComponent(SectionList);
out of the class and stopped jumping to the top
Upvotes: 3