[英]React Hooks, setTimeout in useEffect not triggering until end, because of state updates
Context :上下文:
Problem :问题:
function useInterval(callback, delay) {
const savedCallback = React.useRef();
// Remember the latest callback.
React.useEffect(() => {
savedCallback.current = callback;
}, [callback]);
// Set up the interval.
React.useEffect(() => {
function tick() {
savedCallback.current();
}
if (delay !== null) {
let id = setInterval(tick, delay);
return () => clearInterval(id);
}
}, [delay]);
}
const defaultMessages = [
{
message: "message 1",
new: false
},
{
message: "message 2",
new: false
},
{
message: "message 3",
new: true
}
];
export default function App() {
const [messages, setMessages] = React.useState(defaultMessages);
const messagesRef = React.useRef(messages);
messagesRef.current = messages;
// add a new message every 2 seconds
useInterval(() => {
messages.length < 10 &&
setMessages([
...messages,
{ message: `message ${messages.length + 1}`, new: true }
]);
}, 2000);
React.useEffect(() => {
const timer = setTimeout(() => {
// console.log("change all messages from new to old");
const updateMessages = messagesRef.current.map(m => ({
...m,
new: false
}));
setMessages([...updateMessages]);
}, 5000); // if you change this to duration less than 2 seconds then it runs just fine
return () => clearTimeout(timer); // removing the timer, calls it with every message update and seemingly ignores the timeout duration
});
return (
<div className="App">
{messages.map(m => (
<div key={m.message}>
{m.message}, status: {m.new ? "new" : "old"}
</div>
))}
</div>
);
}
Example code : https://codesandbox.io/s/settimeout-resetting-with-updates-ufl3b示例代码: https://codesandbox.io/s/settimeout-resetting-with-updates-ufl3b
Not sure how to approach this with React Hooks api.不知道如何使用 React Hooks api 来解决这个问题。 The timeouts need to persist, five seconds after every update.
超时需要在每次更新后持续 5 秒。 Where as right now every timeout seemed to be canceled or queued by the one that comes after it.
现在,每个超时似乎都被之后的超时取消或排队。 I'm puzzled.
我很困惑。
Thank you!谢谢!
Well, the main problem that I noticed is clearing the timeout on next render , meaning, if you render faster enough, you actually canceling the timeout callback instead of running it.好吧,我注意到的主要问题是清除下一次渲染的超时,这意味着,如果您渲染得足够快,您实际上会取消超时回调而不是运行它。
React.useEffect(() => {
const timer = setTimeout(() => {});
// will clear the timeout on ***next*** render!
return () => clearTimeout(timer);
});
So after fixing it, and using functional updates instead of reference, seems like this code works:因此,在修复它并使用功能更新而不是参考之后,这段代码似乎可以工作:
export default function App() {
const [messages, setMessages] = React.useState(defaultMessages);
// add a new message every 2 seconds
useInterval(() => {
messages.length < 10 &&
setMessages(prev => [
...prev,
{ message: `message ${messages.length + 1}`, new: true }
]);
}, 2000);
React.useEffect(() => {
console.log("rendered");
setTimeout(() => {
setMessages(prev =>
prev.map(m => ({
...m,
new: false
}))
);
}, 3000);
});
return (
<div className="App">
{messages.map(m => (
<div key={m.message}>
{m.message}, status: {m.new ? "new" : "old"}
</div>
))}
</div>
);
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.