以下代码在codesandbox.io网站的控制台内(该版本使用StrictMode
)和下面的代码段中(而不是使用StrictMode
)同时打印两次:
const { useState, useEffect } = React;
function useCurrentTime() {
const [timeString, setTimeString] = useState("");
useEffect(() => {
const intervalID = setInterval(() => {
setTimeString(new Date().toLocaleTimeString());
}, 100);
return () => clearInterval(intervalID);
}, []);
return timeString;
}
function App() {
const s = useCurrentTime();
console.log(s);
return <div className="App">{s}</div>;
}
ReactDOM.render(<App />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.development.js"></script>
演示:https://codesandbox.io/s/gallant-bas-3lq5w?file=/src/App.js(使用StrictMode
)
下面是一个使用生产库的片段;它仍然记录两次:
const { useState, useEffect } = React;
function useCurrentTime() {
const [timeString, setTimeString] = useState("");
useEffect(() => {
const intervalID = setInterval(() => {
setTimeString(new Date().toLocaleTimeString());
}, 100);
return () => clearInterval(intervalID);
}, []);
return timeString;
}
function App() {
const s = useCurrentTime();
console.log(s);
return <div className="App">{s}</div>;
}
ReactDOM.render(<App />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
然而,当我打开开发人员控制台时,我看到每次只打印一次,而且在codesandbox.io的控制台中,我看到它打印一次。
然后,如果我使用create-rect应用程序创建一个独立的React应用程序,并使用上面的代码,每次打印两次。
如何理解这种行为,根据不同的情况打印一两次?我的想法是,如果状态发生变化,那么App
将被重新呈现,并使用新字符串一次,因此它将被打印出来一次。奇怪的是,尤其是为什么它打印了两次,但当开发控制台打开时,它是一次?
据我所知,我们看到的对App
的双重调用是预期行为。来自useState
文档:
退出状态更新
如果将State Hook更新为与当前状态相同的值,React将在不渲染子对象或发射效果的情况下退出。(React使用
Object.is
比较算法。)请注意,React可能仍然需要在退出之前再次渲染该特定组件。这不应该是一个问题,因为React不会不必要地"深入"到树中。如果在渲染时进行昂贵的计算,可以使用
useMemo
对其进行优化。
那里的关键位是"请注意,React可能仍然需要在退出之前再次渲染该特定组件">和";。。。React不会不必要地深入到树中">
事实上,如果我更新您的示例,使其使用子组件来显示时间字符串,我们只会看到每个timeString
值调用一次,而不是App
的两次,即使子组件没有封装在React.memo
:中
const { useState, useEffect } = React;
function useCurrentTime() {
const [timeString, setTimeString] = useState("");
useEffect(() => {
const intervalID = setInterval(() => {
setTimeString(new Date().toLocaleTimeString());
}, 100);
return () => clearInterval(intervalID);
}, []);
return timeString;
}
function ShowTime({timeString}) {
console.log("ShowTime", timeString);
return <div className="App">{timeString}</div>;
}
function App() {
const s = useCurrentTime();
console.log("App", s);
return <ShowTime timeString={s} />;
}
ReactDOM.render(<App />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
当我运行它时,我看到:
App 09:57:14
ShowTime 09:57:14
App 09:57:14
App 09:57:15
ShowTime 09:57:15
App 09:57:15
App 09:57:16
ShowTime 09:57:16
App 09:57:16
App 09:57:17
ShowTime 09:57:17
App 09:57:17
App 09:57:18
ShowTime 09:57:18
App 09:57:18
App 09:57:19
ShowTime 09:57:19
App 09:57:19
App 09:57:20
ShowTime 09:57:20
App 09:57:20
注意,尽管对于timeString
的每个值App
被调用两次,但ShowTime
仅被调用一次。
我应该注意到,这比使用class
组件时更加自动化。如果不实现shouldComponentUpdate
,等效的class
组件将每秒更新10次。:-)
您在CodeSandbox上看到的一些内容很可能是由于StrictMode
,更多信息请点击此处。但对于每个timeString
值,对App
的两个调用只是React在做它的事情。