以下代码在 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>
但是,当我打开 Developer's Console 时,我看到每次只打印一次,并且在 codeandbox.io 的控制台中,我看到它打印了一次。
然后,如果我使用 create-react-app 创建一个独立的 React 应用程序,并使用上面的代码,每次都会打印两次。
如何理解这种行为,根据不同情况打印一次或两次?我的想法是,如果状态发生变化,那么
App
使用新字符串 重新渲染一次 ,所以打印一次。奇怪的是为什么它打印了两次,但是当开发控制台打开时,它是一次?
最佳答案
据我所知,我们看到的双重电话 App
是预期的行为。来自 useState
documentation :
Bailing out of a state update
If you update a State Hook to the same value as the current state, React will bail out without rendering the children or firing effects. (React uses the
Object.is
comparison algorithm.)Note that React may still need to render that specific component again before bailing out. That shouldn’t be a concern because React won’t unnecessarily go “deeper” into the tree. If you’re doing expensive calculations while rendering, you can optimize them with
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
请注意,尽管 App
为 timeString
的每个值调用两次, ShowTime
只调用一次。我应该注意到这比
class
更自动。成分。相当于class
如果您没有实现 shouldComponentUpdate
,组件将每秒更新 10 次. :-)您在 CodeSandbox 上看到的一些内容很可能是由于
StrictMode
, 更多关于 here .但是两次调用 App
对于每个 timeString
值(value)只是 React 做它的事情。
关于javascript - 为什么使用 Hooks 的 ReactJS 组件会根据开发者控制台是否打开而呈现一次或两次?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66525901/