I'm experimenting SSE on React, everything is doing well but component is not re-rendered
SOLUTION ON BOTTOM
Here is the code
import React, { useEffect, useState } from "react";
import axios from "axios";
function App() {
const [streamMessages, setStreamMessages] = useState([]);
const [eventSource, setEventSource] = useState(null);
const fetchData = async () => {
const msgs = await axios.get("http://localhost:3008/message/all/abc123");
setStreamMessages(msgs.data);
};
useEffect(() => {
fetchData();
setEventSource(new EventSource("http://localhost:3008/message/abc123"));
return () => {
if (eventSource) {
eventSource.close();
console.log("eventsource closed");
}
};
}, []);
useEffect(() => {
if (eventSource) {
eventSource.onopen = (event) => {
console.log("connection opened");
};
eventSource.onmessage = (event) => {
console.log("result", event.data);
setStreamMessages((old) => [...old, event.data]);
};
eventSource.onerror = (event) => {
console.log(event.target.readyState);
if (event.target.readyState === EventSource.CLOSED) {
console.log("eventsource closed (" + event.target.readyState + ")");
}
eventSource.close();
};
}
}, [eventSource, streamMessages]);
return (
<div className="App">
<input type="text" />
<button>Envoyer</button>
<div>
{streamMessages.map((message, index) => (
<p key={index}>
{message.message} <em>{message.createdAt}</em>
</p>
))}
</div>
</div>
);
}
export default App;
First useEffect fetch 'initial data' and put them in the state
const fetchData = async () => {
const msgs = await axios.get("http://localhost:3008/message/all/abc123");
setStreamMessages(msgs.data);
};
This work as expected, I can see the list of messages
The second useEffect should catch incoming message and add them to initial array of message
useEffect(() => {
if (eventSource) {
eventSource.onopen = (event) => {
console.log("connection opened");
};
eventSource.onmessage = (event) => {
console.log("result", event.data);
setStreamMessages((old) => [...old, event.data]);
};
eventSource.onerror = (event) => {
console.log(event.target.readyState);
if (event.target.readyState === EventSource.CLOSED) {
console.log("eventsource closed (" + event.target.readyState + ")");
}
eventSource.close();
};
}
}, [eventSource, streamMessages]);
I can see the "console.log" which means the events are well caught, but my list if message 'streamMessages' is not re-rendered. I put this state 'streamMessages' as dependency of useEffect.
I can't see what's wrong with this code
SOLUTION HERE
Ok I finally found the solution I applied some fix of Dennis Vash (thanks to you). But the main problem was I forgot to JSON parse the data sent by the server
Here is the complete, working, code
import React, { useEffect, useState } from "react";
import axios from "axios";
function App() {
const [streamMessages, setStreamMessages] = useState([]);
const [eventSource, setEventSource] = useState(
() => new EventSource("http://localhost:3008/message/abc123")
);
useEffect(() => {
const fetchData = async () => {
const msgs = await axios.get("http://localhost:3008/message/all/abc123");
setStreamMessages(msgs.data);
};
fetchData();
return () => {
if (eventSource) {
eventSource.close();
console.log("eventsource closed");
}
};
}, []);
useEffect(() => {
if (eventSource) {
eventSource.onopen = (event) => {
console.log("connection opened");
};
eventSource.onmessage = (event) => {
setStreamMessages((old) => [...old, JSON.parse(event.data)]);
};
eventSource.onerror = (event) => {
console.log(event.target.readyState);
if (event.target.readyState === EventSource.CLOSED) {
console.log("eventsource closed (" + event.target.readyState + ")");
}
eventSource.close();
};
}
}, [eventSource, streamMessages]);
return (
<div className="App">
<input type="text" />
<button>Envoyer</button>
<div>
{streamMessages.map((message, index) => (
<p key={index}>
{message.message} <em>{message.createdAt}</em>
</p>
))}
</div>
</div>
);
}
export default App;