Im building a chat app with React Native using Expo and I use a Flatlist as child of KeyboardAvoidingView to render the list of messages, the problem is I want to scroll to the bottom when the keyboard is triggered
So I used the Flatlist method ( scrollToEnd ) with useRef hook and my code looks like this :
const ChatBody = ({ messages }) => {
const listRef = useRef(null);
useEffect(() => {
Keyboard.addListener("keyboardWillShow", () => {
setTimeout(() => listRef.current.scrollToEnd({ animated: true }), 100);
});
return () => Keyboard.removeListener("keyboardWillShow");
}, []);
return (
<FlatList
ref={listRef}
keyboardDismissMode="on-drag"
data={messages}
keyExtractor={(item) => item.id || String(Math.random())}
renderItem={({ item }) => <Message {...item} />}
/>
}
The code works just fine at the first render, but when I leave the screen and get back again and trigger the keyboard I get this error :
TypeError : null in not an object (evaluating 'listRef.current.scrollToEnd')
*The reason I added setTimout was because the scrollToEnd for some reason does not work when the keyboard event is triggered. adding setTimeout solved that issue.
The component tree is kinda like this :
StackNavigatorScreen => KeyboardAvoidingView => FlatList
You need to pass your event handler as a second parameter to Keyboard.removeListener
. Since you're only passing in the first argument, your handler is run anyway and before your ref could be set.
const ChatBody = ({ messages }) => {
const listRef = useRef(null);
useEffect(() => {
Keyboard.addListener("keyboardWillShow", onKeyboardWillShow);
return () => Keyboard.removeListener("keyboardWillShow", onKeyboardWillShow);
}, []);
function onKeyboardWillShow() {
setTimeout(() => {
listRef.current.scrollToEnd();
}, 100);
}
return (
<FlatList
ref={listRef}
keyboardDismissMode="on-drag"
data={messages}
keyExtractor={(item) => item.id || String(Math.random())}
renderItem={({ item }) => <Message {...item} />}
/>
)
}