WebSocket в ReactJS устанавливает состояние с пустым массивом - PullRequest
1 голос
/ 08 марта 2020

Заранее спасибо за любые предложения здесь. Я внедряю websocket в ReactJS здесь, и моя цель - заполнить мое состояние history предстоящими событиями onmessage. Как ни странно, 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 на сервере.

Еще раз спасибо, Фелипе.

Ответы [ 2 ]

3 голосов
/ 08 марта 2020

Вы можете просто изменить setHistory([...history, {text: e.data}]) с помощью следующего кода setHistory(history=> [...history, {text: e.data}])

1 голос
/ 08 марта 2020

У вас есть устаревшее закрытие, это распространенная ошибка, которая часто допускается при использовании хуков с зависимостями (таких как useEffect, useMemo, useCallback). Взгляните здесь .

Решение состоит в том, чтобы всегда включал все переменные, которые вы собираетесь использовать в обратном вызове useEffect, в список зависимостей . В вашем случае их трудно обнаружить, потому что большинство из них скрыты внутри функции connectToWebSocket, которая обновляется при каждом рендеринге, кроме ... вызываемой useEffect является та, которая была создана в первый рендеринг, и обратный вызов onmessage закрывается по состоянию, которое компонент имел при первом рендеринге.

Поскольку вы используете WebSocket, недостаточно просто useEffect каждый раз, когда ваш history изменения, потому что тогда у вас будет несколько соединений (по одному на каждое изменение), вам также нужно будет отключиться при вызове следующего эффекта, и вы можете сделать это, возвращая функцию из обратного вызова useEffect, который отключается от WebSocket , так что эффект может подключиться снова. Это также пример, который концептуально объясняется в документах React при показе примера об useEffect.

Что-то, что часто предлагается, чтобы избежать такой ситуации, это

  1. Определите функцию, которую вы будете вызывать в обратном вызове useEffect внутри самого обратного вызова, а не вне его;
  2. Используйте свой любимый линтер, чтобы предупредить вас о пропавших зависимостях в вашем useEffect (или аналогичные) звонки. Если вы попробуете create-react-app, это уже настроено для вас.

Также, как предполагает @ ako-javakhishvili, всякий раз, когда ваше следующее состояние зависит от предыдущего значения самого состояния, вызывайте функцию setter. с обратным вызовом вместо немедленного значения. Это решит вашу проблему в этом случае, но у вас все еще будет несвежее закрытие, и вы должны позаботиться об этом.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...