React的异步更新造成race condition问题

当我们使用websocket或SignalR进行接收消息时,逃不掉会遇到异步更新问题。由于setState和useState是异步执行的(不会立即更新state的结果), 多次同时执行setState和useState,由于不能及时得到结果,造成新内容(部分更新的时候)总被后面的内容覆盖。

比如下面代码,当同时收到多条消息时,就会造成数据覆盖:

import {atom} from "recoil"
import {useRecoilState} from "recoil"
const WebSocket = require("isomorphic-ws");

const statusAtom = atom<string[]>({
    key: "statusAtom",
    default: []
});


function Component() {
    const [status, setStatus] = useRecoilState(statusAtom);

    const functionCalledAfterButtonPress = () => {
        ws.onmessage = function incoming(res: any) {
            console.log(res.data); // for debugging
            
            setStatus([...status, res.data]);
        };
    }
}


为了解决这个问题,我做了如下实验:

试验一

import { useState, useEffect } from "react";

const UseStateTest1 = (props) => {
  const [value, setValue] = useState({ a: "xx", b: "yy", c: "zz" });

  useEffect(() => {
    setTimeout(() => {
      setA();
      setB();
      setC();
    }, 3000);
  }, []);

  const setA = () => {
    setValue({ ...value, a: "aaa" });
  };

  const setB = () => {
    setValue({ ...value, b: "bbb" });
  };

  const setC = () => {
    setValue({ ...value, c: "ccc" });
  };
  console.log("Render 1------------------");
  return (
    <div>
      <div>{value.a}</div>
      <div>{value.b}</div>
      <div>{value.c}</div>
    </div>
  );
};

得到的结果果然是:

xx
yy
ccc


后来发现,react的setState可以接受传入箭头函数的方式,于是有了

试验二

import { useState, useEffect } from "react";

const UseStateTest2 = (props) => {
  const [value, setValue] = useState({ a: "xx", b: "yy", c: "zz" });

  useEffect(() => {
    setTimeout(() => {
      setA();
      setB();
      setC();
    }, 3000);
  }, []);

  const setA = () => {
    setValue((pre) => {
      return { ...pre, a: "aaa" };
    });
  };

  const setB = () => {
    setValue((pre) => {
      return { ...pre, b: "bbb" };
    });
  };

  const setC = () => {
    setValue((pre) => {
      return { ...pre, c: "ccc" };
    });
  };
  console.log("Render 2------------------");
  return (
    <div>
      <div>{value.a}</div>
      <div>{value.b}</div>
      <div>{value.c}</div>
    </div>
  );
};

这次得到了我想要的结果,前两次的状态也保留了:

aaa
bbb
ccc

因此遇到这种异步操作,依赖上一次更新的值,继续进行下一次更新。我们可以传入lambda表达式的形式,它会等上一次更新完之后,拿上一次结果,继续下一次更新。

 

代码地址
https://codesandbox.io/s/react-hook-sample-1vvmc?file=/src/UseStateTest.js

相关标签:
  • race condition
  • reactjs
0人点赞

发表评论

当前游客模式,请登陆发言

所有评论(0)