简体   繁体   English

反应 useState 钩子没有持续更新

[英]React useState hook not consistently updating

I started integrating websockets into an existing React/Django app following along with this example (accompanying repo here ).我开始将 websockets 集成到现有的 React/Django 应用程序中,并遵循此示例此处随附 repo)。 In that repo, the websocket interface is in websockets.js , and is implemented in containers/Chat.js .在该仓库中,websocket 接口位于websockets.js中,并在containers/Chat.js中实现。

I can get that code working correctly as-is.我可以让该代码按原样正常工作。

I then started re-writing my implementation to use Hooks, and hit a little wall.然后我开始重写我的实现以使用 Hooks,并碰了壁。 The data flows through the socket correctly, arrives in the handler of each client correctly, and within the handler can read the correct state.数据正确流过socket,正确到达每个客户端的handler,handler内可以读取到正确的state。 Within that handler, I'm calling my useState function to update state with the incoming data.在该处理程序中,我调用我的 useState function 以使用传入数据更新 state。

Originally I had a problem of my single useState function within addMessage() inconsistently firing (1 in 10 times?).最初我在addMessage()中的单个useState function 不一致触发(10 次中有 1 次?)。 I split my one useState hook into two (one for current message, one for all messages).我将我的一个useState挂钩分成两个(一个用于当前消息,一个用于所有消息)。 Now in addMessage() upon receiving data from the server, my setAllMessages hook will only update the client where I type the message in - no other clients.现在在addMessage()从服务器接收数据后,我的setAllMessages钩子只会更新我输入消息的客户端 - 没有其他客户端。 All clients receive/can log the data correctly, they just don't run the setAllMessages function.所有客户端都接收/可以正确记录数据,他们只是不运行setAllMessages function。

If I push to an empty array outside the function, it works as expected.如果我推送到 function 之外的空数组,它会按预期工作。 So it seems like a problem in the function update cycle, but I haven't been able to track it down.所以这似乎是 function 更新周期中的一个问题,但我一直无法追踪它。

Here's my version of websocket.js :这是我的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;

And here's my version of Chat.js这是我的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>
  );
}

There is no need to rewrite everything into functional components with hooks.无需使用钩子将所有内容重写为功能组件。

You should decompose it functionally - main (parent, class/FC) for initialization and providing [data and] methods (as props) to 2 functional childrens/components responsible for rendering list and input (new message).您应该在功能上分解它- 用于初始化的主(父级、类/FC)并向负责呈现列表和输入(新消息)的 2 个功能性子级/组件提供 [数据和] 方法(作为道具)。

If you still need it... useEffect is a key... as all code is run on every render in functional components... including function definitions , redefinitions, new refs, duplications in callbacks array etc.如果您仍然需要它... useEffect是一个关键...因为所有代码都在功能组件中的每个渲染上运行...包括 function 定义、重新定义、新参考、回调数组中的重复等。

You can try to move all once defined functions into useEffect您可以尝试将所有曾经定义的函数移动到useEffect

useEffect(() => {

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

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

  waitForSocketConnection(() => {
    ...
  }

}, [] ); // <<< RUN ONCE

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM