1

I'm building a messaging application. I'm retrieving the message log from my backend every 5 seconds with setTimeout and storing it in a state variable. I'm having it also scroll to the bottom of the chat window every timeout, but I am trying to make it only occur when there is a new message in the response object. The issue is that I cannot access the state variable inside my function that retrieves it. Ideally I would compare the response object's length to the state variable's current length to determine if there is an new response.

Here's my code:


  import React from "react"
  import axios from "axios"

  export default function Messaging(props) {

    const [messages, setMessages] = React.useState([])

    const getMessages = () => {

      const config = {
        method: "get",
        url: "localhost:3001/get-messages",
      }

      axios(config)
        .then((response) => {

         console.log(messages.length) // prints 0 in reference to the initial state value

          if (response.data.get.records.length !== messages.length) {
            // function to scroll to the bottom of the chat window
          }

          setMessages(response.data.get.records)
          setTimeout(getMessages, 5000)
          
     
        })
    }

    console.log(messages.length) // prints actual value correctly

    // Initial retrieval of chat log
    React.useEffect(() => {
      getMessages()
    }, [])
  }

I'm open to better suggestions in handling the logic as well. Thanks in advance!

1 Answers1

3

setMessages will update messages in the next render

With that in mind, you could create an Effect that whenever messages.length changes, scrolls to your element

  import React from "react"
  import axios from "axios"

  export default function Messaging(props) {

    const [messages, setMessages] = React.useState([])

    React.useEffect(() => {
      // store the id to clear it out if required
      let timeoutId

      const getMessages = () => {
        const config = {
          method: "get",
          url: "localhost:3001/get-messages",
        }
      
        axios(config)
          .then((response) => {
            // store the messages
            setMessages(response.data.get.records)
            // here we tell to poll every 5 seconds
            timeoutId = setTimeout(getMessages, 5000)
          })
      }

      getMessages()
      
      return () => {
        if (timeoutId) {
          clearTimeout(timeoutId)
        }
      }
    }, [setMessages])
    
    React.useEffect(() => {
      // code here to scroll
      // this would only trigger if the length of messages changes
    }, [messages.length])
  }

I also added the cleanup in your first effect, so when your component unmounts you cleanup the timeout and you avoid any extra request

Gonzalo.-
  • 12,512
  • 5
  • 50
  • 82