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);
}
}}
/>
);
}