Reputation: 137
I was experiencing some weird behavior in my React app, where setEffect with a state (say state1) as a second argument was changing another variable (not a state) without setState1 being ever called. So I made the following code to see if the same would happen, and it did:
import React, { useState, useEffect } from 'react';
function App() {
let [state1, setState1] = useState('');
let count = 0;
useEffect(() => {
count = count + 1000;
console.log('USEEFFECT_count: ' + count);
}, [state1]);
setInterval(() => {
console.log('SETINTERVAL_count: ' + count);
}, 1000);
return (
<div className="App">
</div>
);
}
export default App;
Basically, the variable count should receive the value of 1000 only when setState1 is called (which never happens) to change the value of state1. But the return of the console is
SETINTERVAL_count: 1000
SETINTERVAL_count: 0
SETINTERVAL_count: 1000
SETINTERVAL_count: 0
SETINTERVAL_count: 1000
SETINTERVAL_count: 0
SETINTERVAL_count: 1000
SETINTERVAL_count: 0
SETINTERVAL_count: 1000
SETINTERVAL_count: 0
And the console.log inside the useEffect is never called, as expected. But if that's the case, then:
If I add a state2 and another useEffect that sums 5000 to count, it adds to the 1000 from the first useEffect :
import React, { useState, useEffect } from 'react';
function App() {
let [state1, setState1] = useState('');
let [state2, setState2] = useState('');
let count = 0;
useEffect(() => {
count = count + 1000;
console.log('USEEFFECT1_count: ' + count);
}, [state1]);
useEffect(() => {
count = count + 5000;
console.log('USEEFFECT2_count: ' + count);
}, [state2]);
setInterval(() => {
console.log('SETINTERVAL_count: ' + count);
}, 1000);
return (
<div className="App">
</div>
);
}
export default App;
The result of the console.log:
SETINTERVAL_count: 6000
SETINTERVAL_count: 0
SETINTERVAL_count: 6000
SETINTERVAL_count: 0
SETINTERVAL_count: 6000
SETINTERVAL_count: 0
SETINTERVAL_count: 6000
SETINTERVAL_count: 0
Upvotes: 2
Views: 395
Reputation: 29282
Their's nothing weird about the output you are getting with your code.
In your code, useEffect
hook will execute only once, i.e. after the initial render. Then it will only execute if state1
is changed but that never happens in your code, so useEffect
executes only once and sets count
to 1000
.
And the console.log inside the useEffect is never called, as expected
No, it will be called. useEffect
will execute after the initial render, that is why count
's value becomes 1000
. Having state1
in the dependency array of useEffect
hook doesn't means that it will only execute if state1
is changed. useEffect
always executes after the initial render, irrespective of whether you pass the dependency array or not or if you pass a non-empty dependency array.
Now, the question of why setInterval
is logging two values of count
?
That is because there are two intervals set in your code. Once during the intial render when count
is 0
and second because of the Strict Mode
which renders your component twice.
As setInterval()
is called at the top level from within your component, a new interval will be set whenever the component re-renders. As your component renders two times, two intervals are set. Callback function of each interval has a closure over the count
when callback function is defined.
In the index.js
file, you will see the App
component wrapped in React.StrictMode
as shown below:
<React.StrictMode>
<App/>
</React.StrictMode>
If you remove the React.StrictMode
component, you will see that only one value of the count
will be logged to the console
Output without Strict Mode:
USEEFFECT_count: 1000
SETINTERVAL_count: 1000
SETINTERVAL_count: 1000
SETINTERVAL_count: 1000
SETINTERVAL_count: 1000
Upvotes: 1
Reputation: 84912
Your call to setInterval
is in the body of the component, so it will happen every time the component renders. Your component is rendering twice, so it's setting up two intervals. Each interval is referring to a different count
variable. One of the count
s is at 1000, because the useEffect
corresponding to that render has run. The other is at 0, because it's useEffect
has not run (since state1 didn't change).
The reason there are two renders is likely because you're using react strict mode, which deliberately does double renders. This is done to help highlight bugs where your code is dependent on the number of times the component renders. So it seems to be doing its job, as it highlighted your dependency on the number of renders.
Upvotes: 1