Search code examples
reactjsfirebaseuse-state

My React component is re-rendering, cannot figure out why


I'm building a React App with Firebase and my component is constantly re-rendering.
I tried some things that didn't work (because they couldn't work, like settig useState inside the callback). This is the code, please help!

const [user, setUser] = useState(null);
const [messages, setMessages] = useState([]);
const [input, setInput] = useState('');

const msgRef = ref(db, 'messages');
onValue(msgRef, snapshot => {
  const msg = snapshot.val();
  if (Object.values(msg) != messages) {
    setMessages(Object.values(msg));
  }
})

Also, I know it is re-rendering because of this code beacuse I console.logged it.


Solution

  • If your read the firebase documentation, you see the following

    This method is triggered once when the listener is attached and again every time the data, including children, changes

    When your component renders the following is happening

    1. Attach listener
    2. Fire listener method straight away
    3. setMessages is called
    4. Because setMessages is being called, the component performs another render, which results in
    5. A listener being attached again
    6. The listener method being fired straight away
    7. setMessages is called

    ... And so on

    The body of a functional component should not contain side effects (that is code that does something other than produce the render output). To trigger a side effect when a component first renders, you need to use a useEffect hook. It's going to look something like this:

    const Component = () => {
      const [messages, setMessages] = useState([]);
    
      useEffect(() => {
        const unsubscribe = onValue(msgRef, snapshot => {
          const msgRef = ref(db, 'messages');
          const msg = snapshot.val();
          if (Object.values(msg) != messages) {
            setMessages(Object.values(msg));
          }
        })
        
        // A function that is returned from the useEffect callback 
        // will be called when the component unmounts. 
        // This is the correct way to remove the listener
        // when you don't need it any more
        return unsubscribe
    
      // The empty array passed into the second argument will result in
      // this function *only ever being called once*
      }, [])
    
      // rest of component code
    }