当我在React中使用钩子尝试反例时,我想看看异步操作如何在钩子中工作,所以我将代码如下:
function App() {
const [count, setCount] = useState(0);
var times = count>0?"times":"time";
var txt = count + " " + times;
useEffect(()=>{
setTimeout(()=>{
console.log(txt)
}, 5000)
},[])
return (
<div className="App">
<h1>Clicked {txt}</h1>
<button onClick={()=>{setCount(1+count);}}>Click</button>
</div>
);
}
页面加载后,我多次单击按钮,然后看到计数增加,但是5秒钟后,控制台仍打印出“ 0 time”,我想知道为什么它不访问闭包中的txt变量值范围
谢谢。
function App() {
if(!window.stack) {
window.stack = []
}
const [count, setCount] = useState({v:0});
window.stack.push(count);
var times = count.v>0?"times":"time";
var txt = count.v + " " + times;
return (
<div className="App">
<h1>Clicked {txt}</h1>
<button onClick={()=>{setCount({v:1+count.v});}}>Click</button>
</div>
);
}
答案 0 :(得分:3)
每次组件渲染或重新渲染时,App
都会再次运行。每次重新渲染都会创建一个新的count
变量。
useEffect
仅在初始渲染上运行,因为其依赖项数组为[]
。在初始渲染中,count
是0
。如果单击该按钮,组件将被重新渲染,但是useEffect
中的超时仍然仅关闭初始渲染的count
变量。
如果您需要类似的东西,您也可以使用ref,对于所有渲染器都有稳定的引用,因此初始渲染器的闭包可以引用在以后的渲染器中发生突变的对象:
const App = () => {
const [count, setCount] = React.useState(0);
const times = count > 0 ? "times" : "time";
const txt = count + " " + times;
const txtRef = React.useRef(txt);
txtRef.current = txt;
React.useEffect(() => {
setTimeout(() => {
console.log(txtRef.current);
}, 5000)
}, []);
return (
<div className="App">
<h1>Clicked {txt}</h1>
<button onClick={()=>{setCount(1+count);}}>Click</button>
</div>
);
}
ReactDOM.render(<App />, document.querySelector('.react'));
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div class='react'></div>
另一种(可能更糟,更复杂)的选择是在clearTimeout
的清理中调用useEffect
,并在每个渲染器上调用setTimeout
,将剩余的超时时间传递给渲染器。通过检查Date.now()
呈现。 (这对于频繁重新渲染的组件效果不佳)