我正在 React 上试验 SSE,一切正常,但组件没有重新渲染
底部解决方案
这是代码
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;
首先 useEffect 获取“初始数据”并将它们放入状态
const fetchData = async () => {
const msgs = await axios.get("http://localhost:3008/message/all/abc123");
setStreamMessages(msgs.data);
};
这项工作按预期进行,我可以看到消息列表
第二个 useEffect 应该捕获传入的消息并将它们添加到消息的初始数组中
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]);
我可以看到“console.log”,这意味着事件被很好地捕获,但是如果消息“streamMessages”没有重新呈现,我的列表。 我把这个状态 'streamMessages' 作为 useEffect 的依赖。
我看不出这段代码有什么问题
此处的解决方案
好的,我终于找到了解决方案 我应用了 Dennis Vash 的一些修复(感谢你)。 但主要的问题是我忘记对服务器发送的数据进行 JSON 解析
这是完整的、有效的代码
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;
答案 0 :(得分:0)
无法保证它有效,因为服务器上可能存在错误(没有可重现的示例)。
所以我修正了一些错误,有两个片段,一个有效,错误注释。
已修复:
function App() {
const [streamMessages, setStreamMessages] = useState([]);
const [eventSource, setEventSource] = useState(
() => new EventSource("http://localhost:3008/message/abc123")
);
// Fetch data init
useEffect(() => {
const fetchData = async () => {
const msgs = await axios.get("http://localhost:3008/message/all/abc123");
setStreamMessages(msgs.data);
};
fetchData();
}, []);
// Eventsource setup
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]);
return <>...</>;
}
评论中的错误(至少我认为那些是错误):
function App() {
const [streamMessages, setStreamMessages] = useState([]);
const [eventSource, setEventSource] = useState(null);
// should be in scope of useEffect callback
// not a mistake in this case, but usually has a closure on some stale data
const fetchData = async () => {
const msgs = await axios.get("http://localhost:3008/message/all/abc123");
setStreamMessages(msgs.data);
};
useEffect(() => {
fetchData();
// unnecessary render, should be as initial value
setEventSource(new EventSource("http://localhost:3008/message/abc123"));
return () => {
// closure on eventSource == null value
// Should get lint warning here
if (eventSource) {
eventSource.close();
console.log("eventsource closed");
}
};
}, []);
useEffect(() => {
// always truthy on streamMessages change
// therefore will reinit on every streamMessages render
// should run only once
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();
};
}
// mistake, no need streamMessages in dep array
// should get lint warning here
}, [eventSource, streamMessages]);
return <>...</>;
}