Search code examples
react-nativereact-hookschatreact-native-flatlist

React Native Chat App, Flatlist useRef is null


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


Solution

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