React useState hook не обновляется постоянно - PullRequest
0 голосов
/ 05 апреля 2020

Я начал интегрировать веб-сокеты в существующее приложение React / Django, следуя наряду с этим примером (сопровождающее репо здесь ). В этом репо интерфейс веб-сокета находится в websockets.js и реализован в containers/Chat.js.

. Я могу заставить этот код работать правильно, как есть.

Затем я начал переписывать свою реализацию, чтобы использовать Hooks, и ударил по маленькой стене. Данные проходят через сокет корректно, корректно поступают в обработчик каждого клиента и внутри обработчика могут прочитать правильное состояние. В этом обработчике я вызываю свою функцию useState для обновления состояния входящими данными.

Изначально у меня была проблема с моей единственной useState функцией при addMessage() непостоянном срабатывании (1 в 10 раз?). Я разделил один useState хук на два (один для текущего сообщения, один для всех сообщений). Теперь в addMessage() после получения данных с сервера мой хук setAllMessages обновит только клиента, в который я ввожу сообщение, - никаких других клиентов. Все клиенты получают / могут правильно регистрировать данные, они просто не запускают функцию setAllMessages.

Если я добавлю sh в пустой массив вне функции, он будет работать, как и ожидалось. Так что это похоже на проблему в цикле обновления функций, но я не смог отследить ее.

Вот моя версия websocket.js:

class WebSocketService {
  static instance = null;

  static getInstance() {
    if (!WebSocketService.instance) {
      WebSocketService.instance = new WebSocketService();
    }
    return WebSocketService.instance;
  }

constructor() {
    this.socketRef = null;
    this.callbacks = {};
  }

  disconnect() {
    this.socketRef.close();
  }

  connect(chatUrl) {
    const path = `${URLS.SOCKET.BASE}${URLS.SOCKET.TEST}`;
    this.socketRef = new WebSocket(path);

    this.socketRef.onopen = () => {
      console.log('WebSocket open');
    };

    this.socketRef.onmessage = e => {
      this.socketNewMessage(e.data);
    };

    this.socketRef.onerror = e => {
      console.log(e.message);
    };

    this.socketRef.onclose = () => {
      this.connect();
    };
  }

  socketNewMessage(data) {
    const parsedData = JSON.parse(data);
    const { command } = parsedData;

    if (Object.keys(this.callbacks).length === 0) {
      return;
    }

    Object.keys(SOCKET_COMMANDS).forEach(clientCommand => {
      if (command === SOCKET_COMMANDS[clientCommand]) {
        this.callbacks[command](parsedData.presentation);
      }
    });
  }

  backend_receive_data_then_post_new(message) {
    this.sendMessage({
      command_for_backend: 'backend_receive_data_then_post_new',
      message: message.content,
      from: message.from,
    });
  }

  sendMessage(data) {
    try {
      this.socketRef.send(JSON.stringify({ ...data }));
    } catch (err) {
      console.log(err.message);
    }
  }



 addCallbacks(allCallbacks) {
    Object.keys(SOCKET_COMMANDS).forEach(command => {
      this.callbacks[SOCKET_COMMANDS[command]] = allCallbacks;
    });
  }

  state() {
    return this.socketRef.readyState;
  }
}

const WebSocketInstance = WebSocketService.getInstance();

export default WebSocketInstance;

А вот моя версия Chat.js

export function Chat() {
  const [allMessages, setAllMessages] = useState([]);
  const [currMessage, setCurrMessage] = useState('');

  function waitForSocketConnection(callback) {
    setTimeout(() => {
      if (WebSocketInstance.state() === 1) {
        callback();
      } else {
        waitForSocketConnection(callback);
      }
    }, 100);
  }

  waitForSocketConnection(() => {
    const allCallbacks = [addMessage];
    allCallbacks.forEach(callback => {
      WebSocketInstance.addCallbacks(callback);
    });
  });


/*
 * This is the problem area
 * `incoming` shows the correct data, and I have access to all state
 * But `setAllMessages` only updates on the client I type the message into
 */
  const addMessage = (incoming) => {
    setAllMessages([incoming]);
  };

  // update with value from input
  const messageChangeHandler = e => {
    setCurrMessage(e.target.value);
  };

  // Send data to socket interface, then to server
  const sendMessageHandler = e => {
    e.preventDefault();
    const messageObject = {
      from: 'user',
      content: currMessage,
    };
    setCurrMessage('');
    WebSocketInstance.backend_receive_data_then_post_new(messageObject);
  };

  return (
    <div>
      // rendering stuff here
    </div>
  );
}

1 Ответ

1 голос
/ 05 апреля 2020

Нет необходимости переписывать все в функциональные компоненты с помощью хуков.

Вы должны разложить его функционально - основной (родительский, класс / F C) для инициализации и предоставления [data и] методов (в качестве реквизитов) двум функциональным дочерним элементам / компонентам, отвечающим за список рендеринга и ввод (новое сообщение).

Если вам все еще это нужно ... useEffect - это ключ ... как весь код запускается на каждом рендере в функциональных компонентах. .. включая определения функций , переопределения, новые ссылки, дубликаты в массиве обратных вызовов и т. Д. c.

Вы можете попробовать переместить все когда-то определенные функции в useEffect

useEffect(() => {

  const waitForSocketConnection = (callback) => {
    ...
  }

  const addMessage = (incoming) => {
    setAllMessages([incoming]);
  };

  waitForSocketConnection(() => {
    ...
  }

}, [] ); // <<< RUN ONCE
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...