I\'m using a FlatList
where each row can be of different height (and may contain a mix of both text and zero or more images from a remote server).
I cannot
So what I think you can do and what you already have the outlets for is to store a collection by the index of the rows layouts onLayout
. You'll want to store the attributes that's returned by getItemLayout: {length: number, offset: number, index: number}
.
Then when you implement getItemLayout
which passes an index you can return the layout that you've stored. This should resolve the issues with scrollToIndex
. Haven't tested this, but this seems like the right approach.
Use scrollToOffset() instead:
export default class List extends React.PureComponent {
// Gets the total height of the elements that come before
// element with passed index
getOffsetByIndex(index) {
let offset = 0;
for (let i = 0; i < index; i += 1) {
const elementLayout = this._layouts[i];
if (elementLayout && elementLayout.height) {
offset += this._layouts[i].height;
}
}
return offset;
}
// Gets the comment object and if it is a comment
// is in the list, then scrolls to it
scrollToComment(comment) {
const { list } = this.props;
const commentIndex = list.findIndex(({ id }) => id === comment.id);
if (commentIndex !== -1) {
const offset = this.getOffsetByIndex(commentIndex);
this._flatList.current.scrollToOffset({ offset, animated: true });
}
}
// Fill the list of objects with element sizes
addToLayoutsMap(layout, index) {
this._layouts[index] = layout;
}
render() {
const { list } = this.props;
return (
<FlatList
data={list}
keyExtractor={item => item.id}
renderItem={({ item, index }) => {
return (
<View
onLayout={({ nativeEvent: { layout } }) => {
this.addToLayoutsMap(layout, index);
}}
>
<Comment id={item.id} />
</View>
);
}}
ref={this._flatList}
/>
);
}
}
onLayout={({ nativeEvent: { layout } }) => this._layouts[index] = layout}
When it is necessary to scroll the screen to the element, I summarize the heights of all the elements in front of it and get the amount to which to scroll the screen (getOffsetByIndex method).
I use the scrollToOffset method:
this._flatList.current.scrollToOffset({ offset, animated: true });
(this._flatList is ref of FlatList)
Have you tried scrollToEnd
?
http://facebook.github.io/react-native/docs/flatlist.html#scrolltoend
As the documentation states, it may be janky without getItemLayout
but for me it does work without it
I did not find any way to use getItemLayout
when the rows have variable heights , So you can not use initialScrollIndex
.
But I have a solution that may be a bit slow:
You can use scrollToIndex
, but when your item is rendered . So you need initialNumToRender
.
You have to wait for the item to be rendered and after use scrollToIndex
so you can not use scrollToIndex
in componentDidMount
.
The only solution that comes to my mind is using scrollToIndex
in onViewableItemsChanged
. Take note of the example below :
In this example, we want to go to item this.props.index
as soon as this component is run
constructor(props){
this.goToIndex = true;
}
render() {
return (
<FlatList
ref={component => {this.myFlatList = component;}}
data={data}
renderItem={({item})=>this._renderItem(item)}
keyExtractor={(item,index)=>index.toString()}
initialNumToRender={this.props.index+1}
onViewableItemsChanged={({ viewableItems }) => {
if (this.goToIndex){
this.goToIndex = false;
setTimeout(() => { this.myFlatList.scrollToIndex({index:this.props.index}); }, 10);
}
}}
/>
);
}