Home > database >  setState is changed but the variable assigned to it can not get the updated value
setState is changed but the variable assigned to it can not get the updated value

Time:08-31

developing a chat app with a loader to wait until receiving the response. According to console.log in the app the isLoading state changes correctly however inside the receiver's object the isLoaded key is not gonna getting update and only show the initial value of the assigned state. As a result the loader doesn't show in the UI. You can find the code below.

const [messagesList, setMessagesList] = useState([]);
const [isLoading, setIsLoading] = useState(false);
const [currentMessage, setCurrentMessage] = useState("");

  const handleSend = () => {
    if (currentMessage !== "") {
      setIsLoading(true);
      setMessagesList([
        ...messagesList,
        {
          dataType: "text",
          item: {
            id: uuidv4(),
            type: "sender",
            message: currentMessage,
          },
        },
      ]);
      axios
        .post(`${BASE_URL}`, {
          data: currentMessage,
        })
        .then((response) => {          
          setIsLoading(false);
          setMessagesList([
            ...messagesList,
            {
              dataType: "text",
              item: {
                id: uuidv4(),
                type: "sender",
                message: currentMessage,
              },
            },
            {
              dataType: "text",
              item: {
                id: uuidv4(),
                type: "reciever",
                isLoaded: isLoading,
                message: response.data.data.answers
              },
            },
          ]);
          setCurrentMessage("");
        })
        .catch((error) => {
          setIsLoading(true);
          setMessagesList([
            ...messagesList,
            {
              dataType: "text",
              item: {
                id: uuidv4(),
                type: "sender",
                message: currentMessage,
              },
            },
            {
              dataType: "text",
              item: {
                id: uuidv4(),
                type: "reciever",
                isLoaded: isLoading,
                message: "something went wrong"
              },
            },
          ]);
          setCurrentMessage("");
          setIsLoading(false);
        });
      setCurrentMessage("");
    } 
  };

return (
<>
       <textarea
        type="text"
         placeholder="ask me ..."
         rows={1}
         cols={1}  
         onChange={(e) => setCurrentMessage(e.target.value)}
         value={currentMessage}    
         autoFocus
 />
<button onClick={ ()=> handleSend }> Sned </button>

   {messagesList.map(({ dataType, item }) => {
                if (dataType === "text") {
                  if (item.type === "sender") {
                    return (
                      <p style={{ color: "red" }}>{item.message}</p>
                    );
                  }
                  if (item.type === "reciever") {
                    if (item.isLoaded) {
                      return <p> laoding</p>;
                    } else {
                      return <p> {item.message} </p>;
                    }
                  }
                }
              })}
</>
    
)

CodePudding user response:

The problem here likely stems from the fact that you are using the value of isLoading right after setting it via setLoading.

Setting the state in React acts like an async function.
Meaning that the when you set the state and try to use it's value right after,
you will likely get the previous value of that state.

In this case, try to make the relevant messagesList values independent of isLoading.

Example:

.then((response) => {
  const newIsLoading = false;
  setIsLoading(newIsLoading);
  setMessagesList([
    ...messagesList,
    {
      // Some code...
    },
    {
      dataType: "text",
      item: {
        id: uuidv4(),
        type: "reciever",
        isLoaded: newIsLoading,
        message: response.data.data.answers
      },
    },
  ]);
  setCurrentMessage("");
})

CodePudding user response:

State updates are dispatched asyncronously. That means that by the time your code reaches your second setMessagesList, that is the last one considered and thus dispatched.

setMessagesList([...]) // to be dispatched
axios.post((resp)=> resp.json().then(data=> setMessagesList([...]) //oops, not that one, this one

Probably, your console.log has to do with that. If you want to see the updated value, you will need to add it as an effect

useEffect(()=> console.log(messagesList), [messagesList])

Lastly, you may want to move shared code by .then and .catch to a .finally:

.finally(()=> {
    setCurrentMessage("");
    setIsLoading(false);
}
  • Related