当我们使用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
发表评论
所有评论(0)