【问题标题】:WebSocket in ReactJS is setting state with empty arrayReactJS 中的 WebSocket 使用空数组设置状态
【发布时间】:2023-03-27 10:05:01
【问题描述】:

在此先感谢您的任何建议。 我在这里在 ReactJS 中实现 websocket,我的目标是用即将到来的“onmessage”事件填充我的 history 状态。对我来说奇怪的是,setHistory 在收到来自“websocket.onmessage”事件的消息后正在清空我的数组。它甚至消除了我的初始状态。简化版如下:

function NotificationWebSocket ({object_id}) {
const [history, setHistory] = useState([{text: "The first notification"}]) //

    function connectToWebSocket() {
        const wsStart = (window.location.protocol === "https:" ? "wss://" : "ws://")
        const url = 'localhost:8000/live/'
        let socket = new ReconnectingWebSocket(wsStart + url + object_id)
        socket.onmessage = e => {
            console.log(e.data) // logs the received data correctly
            setHistory([...history, {text: e.data}]) // ?? this is setting my history state array to null... ??
            setHistory(history.append({text: e.data})) //also cleans my history array here.
        }
    }

    useEffect(() => {
        connectToWebSocket()
    },[]) // so that we only connect to the websocket once

    return (
        <div>
            We are on a live connection to the server.
            {history.map(item=> <div>{item.text}</div>)}
        </div>
    )
}

注意事项: 我正在使用 ReconnectingWebSocket 库,尽管我尝试不使用它并且发生了同样的事情。 我的 connectToWebSocket 是从 useEffect 内部触发的,否则我会在服务器中收到几个 websockets 请求。

再次感谢, 费利佩。

【问题讨论】:

    标签: reactjs websocket react-hooks


    【解决方案1】:

    你可以简单地用下面的代码setHistory(history=&gt; [...history, {text: e.data}])更改setHistory([...history, {text: e.data}])

    【讨论】:

    • 好。有用!这是一个范围问题,@juandemarco 也提到过。此外,我还考虑了 juandemarco 的顾虑,并将 connectToWebSocket 引入了 useEffect 本身。这个解决方案似乎比向依赖项添加历史记录和连接/断开连接到 websocket 更直接。
    • 这两种解决方案不是相互的。您应该同时使用它们,因为它们解决不同的问题:)
    【解决方案2】:

    你有一个陈旧的闭包,这是在使用带有依赖项的钩子时经常犯的一个常见错误(例如useEffectuseMemouseCallback)。看看here

    解决方案是始终在依赖项列表中的useEffect 回调中包含您要使用的所有变量。在您的情况下,很难发现它们,因为它们中的大多数都隐藏在 connectToWebSocket 函数中,该函数在每次渲染时都会更新,除了... useEffect 调用的函数是在 上创建的函数第一次渲染,onmessage 回调关闭组件在第一次渲染时的状态。

    由于您使用的是WebSocket,因此每次history 更改时仅useEffect 是不够的,因为您将有多个连接(每次更改一个),您还需要在下一个效果时断开连接被调用,您可以通过从与 WebSocket 断开连接的useEffect 回调中返回一个函数来执行此操作,以便效果可以再次连接。这也是 React 文档中在概念上解释的示例,当时显示了一个关于 useEffect 的 example

    通常建议避免这种情况的方法是

    1. 在回调内部而不是在回调外部定义您要在 useEffect 回调中调用的函数;
    2. 使用您最喜欢的 linter 警告您在 useEffect(或类似的)调用中缺少依赖项。如果您尝试create-react-app,这已经为您配置好了。

    另外,正如@ako-javakhishvili 建议的那样,每当您的下一个状态取决于状态本身的先前值时,请使用回调而不是立即值调用 setter 函数。在这种情况下,这将解决您的问题,但您仍然会有一个陈旧的闭包,您也应该注意这一点。

    【讨论】:

    • 谢谢,胡安。我将仔细研究 useEffect 的完整指南。如果我做对了,每次更新历史记录时,我都会连接和断开与 websocket 的连接,对吗?
    • 是的,没错。不过这应该不是问题,只要历史没有太大变化。
    • 完美!很高兴将这个解决方案放在口袋里,也感谢您进一步解释并带来解决方案的链接。欣赏。
    • 不客气!请记住,我在答案中加上粗体不是因为它是可选的和“最佳实践”,而是因为它确实是一个要求。不这样做会导致奇怪且非常难以跟踪的错误,我自己付出了这个代价:)
    • 是的!我已经阅读了 useEffect 的完整指南,现在我的应用程序中有很多内容要介绍。我相信在那之后我的待办事项清单会被清理掉。还有一些事情我需要在编码时更好地理解它。但这确实有帮助。再次感谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-06-09
    • 2022-12-11
    • 1970-01-01
    • 1970-01-01
    • 2017-10-02
    • 2014-09-23
    • 2019-11-11
    相关资源
    最近更新 更多