延迟后反应日志最终状态值
React log final state value after delay
我有一个 MERN 应用程序。在反应方面,我有一个状态。此状态每秒可能会或可能不会更改多次。当状态更新时,我想将状态发送到我的后端服务器 API,这样我就可以将值保存在我的 mongodb 中。这种状态可能每秒改变数百次,这是我希望允许的。但是,我只想最多每 5 秒将此值发送到我的服务器一次。这是为了避免垃圾邮件和阻塞我的 mongodb Atlas 请求。
目前,我尝试过 setInterval、setTimeout,甚至用 while(time
这些都提出了问题:
setInterval 很好,因为我可以用 lastSentValue 检查 currentValue,如果它们不相等 (!==),那么我会将 currentValue 发送到我的服务器。不幸的是,当我设置间隔时,它 returns 调用 setInterval 时存在的初始值。
如果你知道我如何让用户发送垃圾邮件布尔按钮,同时最多每 5 秒从前端 (React) 向后端 (Node) 发送一次更新,并且它发送当前和向上迄今为止的价值然后请分享您的想法,我会尽快测试它们。
我的状态值存储为::
const [aValue, anUpdate] = useState(false);
在我的 React 应用程序中返回的 onClick 方法改变了状态。
function Clicked(){
anUpdate(!aValue);
}
我设置的间隔测试如下所示::
//This is so that the button may be pressed multiple times but the value is only sent once.
const [sent, sentUpdate] = useState(false);
//inside of the clicked method
if(!sent){
sentUpdate(true);
setInterval(()=>{
console.log(aValue);
},1000);
}
我的 setTimeout 非常相似,除了我再添加一个 sentUpdate 并在记录 aValue 后将其重置为 false,这样我可以再次记录超时。
//setInterval and setTimeout expected results in psudocode
aValue=true
first click->set aValue to !aValue (now aValue=false), start timeout/interval, stop setting timeouts/interval until completed
second click->set aValue to !aValue (now aValue=true), do not set timeout/interval as it is still currently waiting.
Completed timeout/interval
Log->false
//expected true as that is the current value of aValue. If logged outside of this timeout then I would receive a true value logged
在完全相反的方向上,我偶然发现的另一个流行的 Whosebug 答案是定义一个函数,该函数使用 while 循环占用计算机时间以伪造 setTimeout/setInterval。
看起来像这样::
function wait(ms){
let start = new Date().getTime();
let end = start;
while(end < start + ms) {
end = new Date().getTime();
}
}
不幸的是,当在上述 if 语句中使用时(为了避免垃圾邮件),我的结果是::
aValue=true
first click->set aValue to !aValue (now aValue=false), start wait(5000), turn off if statement so we don't call many while loops
second click->nothing happens yet - waiting for first click to end.
first click timeout->logged "false"->if statement turned back on
second click that was waiting in que is called now->set aValue to !aValue (now aValue=true), start wait(5000), turn off if statement so we don't call many while loops
second click timeout->logged "true"->if statement turned back on
所以 while 循环方法也不是一个选项,因为它仍然会发送每个按钮按下。当他们垃圾点击时,它只会让客户陷入困境。
我看到的另一种方法是使用 Promise 包装我的 setTimeout 和 setInterval,但这绝不会改变 setTimeout/setInterval 的原始输出。
它看起来像这样::
const promise = new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve(true);
},5000);
});
promise.then(console.log(aValue));
//I also tried resolve(aValue)->promise.then(val=>console.log(val));
对于你试图用循环和回调中的开始间隔做的事情,只会在到达迭代的特定状态值(即初始状态值)上关闭。
解决方案是使用 useEffect
挂钩来处理或“侦听”对依赖项值的更改。每次 state
更新都会触发组件重新渲染并调用 useEffect
挂钩,并且由于依赖项已更新,因此会调用挂钩的回调。
useEffect(() => {
sendStateToBackend(state);
}, [state]);
如果您想限制 sendStateToBackend
实际调用的频率,那么您需要限制调用。这是一个使用 lodash 的 throttle 高阶函数的示例。
import { throttle } from 'lodash';
const sendStateToBackend = throttle((value) => {
// logic to send to backend.
}, 5000);
const MyComponent = () => {
...
useEffect(() => {
sendStateToBackend(state);
}, [state]);
...
更新
如果你想等直到点击按钮开始向后端发送更新,那么你可以使用React ref跟踪最初单击按钮的时间,以触发向后端发送数据。
const clickedRef = React.useRef(false);
const [aValue, anUpdate] = React.useState(false);
React.useEffect(() => {
if (clickedRef.current) {
sendToBackend(aValue);
}
}, [aValue]);
const clicked = () => {
clickedRef.current = true
anUpdate(t => !t);
};
...
另见 Lodash per method packages,因为 package/bundle 大小似乎是个问题。
所以我昨晚脑洞大开,我通过创建一系列超时来解决这个问题,这些超时取消了之前的按钮点击超时,并使用上次超时的剩余值设置了一个新的超时。
const [buttonValue, buttonUpdate] = useState(props.buttonValue);
const [lastButtonValue, lastButtonUpdate] = useState(props.buttonValue);
const [newDateNeededValue, newDateNeededUpdate] = useState(true);
const [dateValue, dateUpdate] = useState();
const [timeoutValue, timeoutUpdate] = useState();
function ButtonClicked(){
let oldDate = new Date().getTime();
if(newDateNeededValue){
newDateNeededUpdate(false);
dateUpdate(oldDate);
}else{
oldDate = dateValue;
}
//clear old timeout
clearTimeout(timeoutValue);
//check if value has changed -- value has not changed, do not set timeout
if(lastButtonValue === !buttonValue){
console.log("same value do not set new timout");
buttonUpdate(!buttonValue);
return;
}
//set timeout
timeoutUpdate(setTimeout(()=>{
console.log("timed out");
lastButtonUpdate(!buttonValue);
newDateNeededUpdate(true);
//This is where I send to my previous file and send to my server with axios
props.onButtonUpdate({newVal:!buttonValue});
clearTimeout(timeoutValue);
}, (5000-(new Date().getTime() - oldDate))));
}
我有一个 MERN 应用程序。在反应方面,我有一个状态。此状态每秒可能会或可能不会更改多次。当状态更新时,我想将状态发送到我的后端服务器 API,这样我就可以将值保存在我的 mongodb 中。这种状态可能每秒改变数百次,这是我希望允许的。但是,我只想最多每 5 秒将此值发送到我的服务器一次。这是为了避免垃圾邮件和阻塞我的 mongodb Atlas 请求。
目前,我尝试过 setInterval、setTimeout,甚至用 while(time 这些都提出了问题: setInterval 很好,因为我可以用 lastSentValue 检查 currentValue,如果它们不相等 (!==),那么我会将 currentValue 发送到我的服务器。不幸的是,当我设置间隔时,它 returns 调用 setInterval 时存在的初始值。 如果你知道我如何让用户发送垃圾邮件布尔按钮,同时最多每 5 秒从前端 (React) 向后端 (Node) 发送一次更新,并且它发送当前和向上迄今为止的价值然后请分享您的想法,我会尽快测试它们。 我的状态值存储为:: 在我的 React 应用程序中返回的 onClick 方法改变了状态。 我设置的间隔测试如下所示:: 我的 setTimeout 非常相似,除了我再添加一个 sentUpdate 并在记录 aValue 后将其重置为 false,这样我可以再次记录超时。 在完全相反的方向上,我偶然发现的另一个流行的 Whosebug 答案是定义一个函数,该函数使用 while 循环占用计算机时间以伪造 setTimeout/setInterval。 看起来像这样:: 不幸的是,当在上述 if 语句中使用时(为了避免垃圾邮件),我的结果是:: 所以 while 循环方法也不是一个选项,因为它仍然会发送每个按钮按下。当他们垃圾点击时,它只会让客户陷入困境。 我看到的另一种方法是使用 Promise 包装我的 setTimeout 和 setInterval,但这绝不会改变 setTimeout/setInterval 的原始输出。
它看起来像这样::const [aValue, anUpdate] = useState(false);
function Clicked(){
anUpdate(!aValue);
}
//This is so that the button may be pressed multiple times but the value is only sent once.
const [sent, sentUpdate] = useState(false);
//inside of the clicked method
if(!sent){
sentUpdate(true);
setInterval(()=>{
console.log(aValue);
},1000);
}
//setInterval and setTimeout expected results in psudocode
aValue=true
first click->set aValue to !aValue (now aValue=false), start timeout/interval, stop setting timeouts/interval until completed
second click->set aValue to !aValue (now aValue=true), do not set timeout/interval as it is still currently waiting.
Completed timeout/interval
Log->false
//expected true as that is the current value of aValue. If logged outside of this timeout then I would receive a true value logged
function wait(ms){
let start = new Date().getTime();
let end = start;
while(end < start + ms) {
end = new Date().getTime();
}
}
aValue=true
first click->set aValue to !aValue (now aValue=false), start wait(5000), turn off if statement so we don't call many while loops
second click->nothing happens yet - waiting for first click to end.
first click timeout->logged "false"->if statement turned back on
second click that was waiting in que is called now->set aValue to !aValue (now aValue=true), start wait(5000), turn off if statement so we don't call many while loops
second click timeout->logged "true"->if statement turned back on
const promise = new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve(true);
},5000);
});
promise.then(console.log(aValue));
//I also tried resolve(aValue)->promise.then(val=>console.log(val));
对于你试图用循环和回调中的开始间隔做的事情,只会在到达迭代的特定状态值(即初始状态值)上关闭。
解决方案是使用 useEffect
挂钩来处理或“侦听”对依赖项值的更改。每次 state
更新都会触发组件重新渲染并调用 useEffect
挂钩,并且由于依赖项已更新,因此会调用挂钩的回调。
useEffect(() => {
sendStateToBackend(state);
}, [state]);
如果您想限制 sendStateToBackend
实际调用的频率,那么您需要限制调用。这是一个使用 lodash 的 throttle 高阶函数的示例。
import { throttle } from 'lodash';
const sendStateToBackend = throttle((value) => {
// logic to send to backend.
}, 5000);
const MyComponent = () => {
...
useEffect(() => {
sendStateToBackend(state);
}, [state]);
...
更新
如果你想等直到点击按钮开始向后端发送更新,那么你可以使用React ref跟踪最初单击按钮的时间,以触发向后端发送数据。
const clickedRef = React.useRef(false);
const [aValue, anUpdate] = React.useState(false);
React.useEffect(() => {
if (clickedRef.current) {
sendToBackend(aValue);
}
}, [aValue]);
const clicked = () => {
clickedRef.current = true
anUpdate(t => !t);
};
...
另见 Lodash per method packages,因为 package/bundle 大小似乎是个问题。
所以我昨晚脑洞大开,我通过创建一系列超时来解决这个问题,这些超时取消了之前的按钮点击超时,并使用上次超时的剩余值设置了一个新的超时。
const [buttonValue, buttonUpdate] = useState(props.buttonValue);
const [lastButtonValue, lastButtonUpdate] = useState(props.buttonValue);
const [newDateNeededValue, newDateNeededUpdate] = useState(true);
const [dateValue, dateUpdate] = useState();
const [timeoutValue, timeoutUpdate] = useState();
function ButtonClicked(){
let oldDate = new Date().getTime();
if(newDateNeededValue){
newDateNeededUpdate(false);
dateUpdate(oldDate);
}else{
oldDate = dateValue;
}
//clear old timeout
clearTimeout(timeoutValue);
//check if value has changed -- value has not changed, do not set timeout
if(lastButtonValue === !buttonValue){
console.log("same value do not set new timout");
buttonUpdate(!buttonValue);
return;
}
//set timeout
timeoutUpdate(setTimeout(()=>{
console.log("timed out");
lastButtonUpdate(!buttonValue);
newDateNeededUpdate(true);
//This is where I send to my previous file and send to my server with axios
props.onButtonUpdate({newVal:!buttonValue});
clearTimeout(timeoutValue);
}, (5000-(new Date().getTime() - oldDate))));
}