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);
}