The following code prints out the same time twice inside the console of codesandbox.io website (that version uses StrictMode
) and also in the snippet below (not using 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>
Demo: https://codesandbox.io/s/gallant-bas-3lq5w?file=/src/App.js (using StrictMode
)
Here's a snippet using production libs; it still logs twice:
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>
However, when I open up the Developer's Console, I see each time printed out only once, and also in the codesandbox.io's console, I see it printed once.
And then if I create a standalone React app using create-react-app, and use the above code, and each time is printed twice.
How is this behavior understood, for printed out once or twice depending on different situations? My thinking was, if the state changes, then App
is re-rendered, with that new string, once, so it is printed out once. What is strange is especially why does it printed out twice but when Dev Console is open, then it is once?
As far as I can tell, the double-call we see to App
is expected behavior. From the 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
.
The key bits there are "Note that React may still need to render that specific component again before bailing out..." and "...React won’t unnecessarily go “deeper” into the tree..."
And indeed, if I update your example so that it uses a child component to show the time string, we only see that child component get called once per timeString
value rather than twice as App
is, even though the child component isn't wrapped in 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>
When I run that, I see:
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
Note how although App
is called twice for each value of timeString
, ShowTime
is only called once.
I should note that this is more automatic than it was with class
components. The equivalent class
component would update 10 times per second if you didn't implement shouldComponentUpdate
. :-)
Some of what you were seeing over on CodeSandbox may well have been due to StrictMode
, more on that here. But the two calls to App
for each timeString
value are just React doing its thing.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With