I'm trying to setup a chat in react native and want to check if the bottom of my FlatList is reached, every time I reach it, so I'm able to scroll to the bottom when the keyboard is opening and the user was on the end of the list before (if she wasn't at the end, I don't want to scroll down).
Of course I could call onEndReached
, but this fires only once when all messages are loaded.
So I was coding following, but asked myself if there is a better way to achieve this:
My states and my FlatList ref:
const [messageInputFocus, setmessageInputFocus] = useState(false);
const [
messagesListDistanceFromEnd,
setMessagesListDistanceFromEnd,
] = useState(0);
const [messagesListScrollYPos, setMessagesListScrollYPos] = useState(0);
const inboxThreadFlatList = useRef<FlatList>(null);
Use useEffect when the input of the message was focued:
useEffect(() => {
if (messagesListDistanceFromEnd === messagesListScrollYPos) {
inboxThreadFlatList.current?.scrollToEnd({ animated: true });
}
}, [messageInputFocus]);
const onScrollMessagesList = (
event: NativeSyntheticEvent<NativeScrollEvent>
) => {
setMessagesListScrollYPos(event.nativeEvent.contentOffset.y);
};
My FlatList with onEndReached and onScroll methods at the bottom:
<FlatList
data={messages.records}
renderItem={({ item, index }) => {
return (
<View
style={
index === messages.length - 1 ? { paddingBottom: 68 } : {}
}
>
{userMe.id === item.sender ? (
<View
style={[
styles.messageContainer,
styles.messageSentContainer,
]}
>
<View>
<Text
style={[styles.messageText, styles.messageSentText]}
>
{item.message}
</Text>
<View
style={[styles.triangle, styles.triangleSent]}
></View>
</View>
</View>
) : (
<View
style={[
styles.messageContainer,
styles.messageReceivedContainer,
]}
>
<View>
<Text
style={[
styles.messageText,
styles.messageReceivedText,
]}
>
{item.message}
</Text>
<View
style={[styles.triangle, styles.triangleReceived]}
></View>
</View>
</View>
)}
</View>
);
}}
ref={inboxThreadFlatList}
keyExtractor={(item, index) => index.toString()}
initialScrollIndex={messages.length - 1}
onScrollToIndexFailed={(info) => {
const wait = new Promise((resolve) => setTimeout(resolve, 500));
wait.then(() => {
inboxThreadFlatList.current?.scrollToIndex({
index: info.index,
animated: true,
});
});
}}
onEndReached={({ distanceFromEnd }) => {
setMessagesListDistanceFromEnd(distanceFromEnd);
}}
onScroll={onScrollMessagesList}
/>
The code works like that. But again, is there a better way, since I set the my scroll position in a state and ask myself, if that's a performance issue?