Search code examples
reactjsuse-effect

React - useEffect hook - componentDidMount to useEffect


I would like to convert this to an useEffect hook:

CODE

componentDidMount () {
   this.messagesRef.on('child_added', snapshot => {
    const message = snapshot.val();
    message.key = snapshot.key;
    this.setState({messages: this.state.messages.concat(message 
  )});
});

UPDATED CODE

const MessageList = () => {
  const [messages, setMessage] = useState([]);
  const [usernames, setUsernames] = useState('');
  const [contents, setContents] = useState('');
  const [roomId, setRoomId] = useState('');

  const messagesRef = MessageList.props.firebase.database().ref('messages');

  useEffect(() => {
    messagesRef.on('child added', snapshot => {
    const message = snapshot.val();
    message.key = snapshot.key;

    const [messages, setMessages] = useState({messages: messages.concat(message)});
  });
 })
}

Right now it's giving me a useState cannot be used in a callback.

How can I address this or convert this properly?


Solution

  • There are a couple of things there. First, to fix the code, you could update your useEffect to this:

    useEffect(() => {
        messagesRef.on('child added', snapshot => {
        const message = snapshot.val();
        message.key = snapshot.key;
    
        setMessages(messages.concat(message)); // See Note 1
    }, []); // See Note 2
    

    Note 1

    The setMessages line is how you update your state. useState is a little bit different from the "old" setState in a sense that will completely replace the state value. React documentation says:

    This is because when we update a state variable, we replace its value. This is different from this.setState in a class, which merges the updated fields into the object.

    Note 2

    React Hooks changes the way we build apps and it is not a real "translation" from the old lifecycles.

    The empty brackets ([]) in the last line, will make your code "similar" to componentDidMount, but most importantly, will make your effect run only once.

    Dan Abramov said (removed some of the original text):

    While you can useEffect(fn, []), it’s not an exact equivalent. Unlike componentDidMount, it will capture props and state. So even inside the callbacks, you’ll see the initial props and state. (...) Keep in mind that the mental model for effects is different from componentDidMount and other lifecycles, and trying to find their exact equivalents may confuse you more than help. To get productive, you need to “think in effects”, and their mental model is closer to implementing synchronization than to responding to lifecycle events.

    Full article about useEffect here.