我正在尝试对我正在处理的项目具有的倒计时组件进行重构。
当我完成逻辑的迁移时,计数器的值不起作用。我决定在codeandbox中从零开始,因此我想最简单的实现,并得出了这样的结论:
import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
import "./styles.css";
function App() {
const [counter, setCounter] = useState(60);
useEffect(() => {
const interval = setInterval(() => setCounter(counter - 1), 1000);
return () => clearInterval(interval);
}, []);
return (
<div className="App">
<h1>Hello CodeSandbox {counter}</h1>
<h2>Start editing to see some magic happen!</h2>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App timerSeconds={360} />, rootElement);
在这里发生的是,counter
的值在间隔的第一次运行后保持在59。
代码和框:https://codesandbox.io/embed/flamboyant-moon-ogyqr
谢谢罗斯的回答,但是真正的问题发生在我将倒计时链接到处理程序时:
import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
import "./styles.css";
function App() {
const [counter, setCounter] = useState(60);
const [countdownInterval, setCountdownInterval] = useState(null);
const startCountdown = () => {
setCountdownInterval(setInterval(() => setCounter(counter - 1), 1000));
};
useEffect(() => {
return () => clearInterval(countdownInterval);
});
return (
<div className="App" onClick={startCountdown}>
<h1>Hello CodeSandbox {counter}</h1>
<h2>Start editing to see some magic happen!</h2>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App timerSeconds={360} />, rootElement);
答案 0 :(得分:1)
在counter
函数的第二个参数(数组)内添加useEffect
变量。当您传递一个空数组时,它将仅在初始渲染时更新一次state
。当您发出HTTP请求或类似内容时,通常使用空数组。 (已编辑第二次迭代)
import React, { useEffect, useState } from "react";
import ReactDOM from "react-dom";
import "./styles.css";
function App() {
const [counter, setCounter] = useState(5);
const [counterId, setCounterId] = useState(null);
useEffect(() => {
return () => clearInterval(counterId);
}, []);
const handleClick = () => {
/*
* I'd take startCountdown and make
* it's own component/hook out of it,
* so it can be easily reused and expanded.
*/
const startCountdown = setInterval(() => {
return setCounter((tick) => {
if (tick === 0) {
clearInterval(counterId);
setCounter(0);
return setCounterId(null);
};
return tick - 1;
});
}, 1000)
setCounterId(startCountdown);
};
return (
<div className="App" onClick={handleClick}>
<h1>Hello CodeSandbox {counter}</h1>
<h2>Start editing to see some magic happen!</h2>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App timerSeconds={360} />, rootElement);
有关此实现的更多信息,请在https://reactjs.org/docs/hooks-effect.html#tip-optimizing-performance-by-skipping-effects阅读有关React Hooks和跳过效果的信息。
答案 1 :(得分:1)
您可以使用useState返回的函数的Functional Updates版本来基于先前状态计算新状态。
您更新的代码如下:
import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
import "./styles.css";
function App() {
const [counter, setCounter] = useState(60);
useEffect(() => {
const interval = setInterval(() => {setCounter(counter => counter - 1);}, 1000);
return () => clearInterval(interval);
}, []);
return (
<div className="App">
<h1>Hello CodeSandbox {counter}</h1>
<h2>Start editing to see some magic happen!</h2>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App timerSeconds={360} />, rootElement);
更新编辑 这是一个通过点击处理程序开始倒计时的版本:
import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
import "./styles.css";
function App() {
const [counter, setCounter] = useState(60);
const [countdownInterval, setCountdownInterval] = useState(null);
const startCountdown = () => {
setCountdownInterval(setInterval(() => setCounter(counter => counter - 1), 1000));
};
useEffect(() => {
return () => clearInterval(countdownInterval);
}, [countdownInterval]);
return (
<div className="App" onClick={startCountdown}>
<h1>Hello CodeSandbox {counter}</h1>
<h2>Start editing to see some magic happen!</h2>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App timerSeconds={360} />, rootElement);