Edit
Okay my bad, I guess the response is simple. It's a javascript matter. I am not passing a function to useState but actually I am re-executing it everytime. So yeah what I am seeing is normal.
I will let my question here anyway.
A codesandbox example will follow.
Question :
Could someone please explain to me why React re-execute the inner function in a useState (or useRef) despite the returned value will be completely ignored (except the first execution) ? Is this the expected behaviour of useState ?
Explanation :
I mean why I see this console log here after each re-render
const [count, setCount] = React.useState((function(){
console.log('Executed !');
return 5;
})())
I used to think this inner function (or values) is the equivalent of a Class Parameters or Component Constructor but it's obviously not the case.
Returning a simple value does the job of course : React.useState(5)
.
But imagine using a third-party class which works with static values at some point or you simply don't want to re-execute it over and over again after each re-render ?
A possible fix for this is using useEffect
with an empty dependency. (The equivalent of componentDidMount
)
But I am asking about the purpose of this execution ?
let count = 0; // Number of component renders
const Test = (props) => {
const [innerCount, setCount] = React.useState((function(){})(
console.log('Executed !'); // Executed after each re-render
count = count 1;
return count;
));
return innerCount; // Will be always 1 !
}
Why the anonymous function will be executed if the returned value is always 1 = the first returned value ?! What's the purpose of this execution ?
This is an example on CodeSandbox
CodePudding user response:
This isn't React-specific behaviour, it's just what Javascript - and almost every mainstream programming language - does.
Before calling a function, it has to evaluate its arguments in order to know what to pass in.
Yes, it so happens that React.useState
is a function that often "ignores" its argument. (I'm not familiar with precisely how it's implemented - clearly it isn't as simple as that as it does need to know when it's the first time it's being executed in that particular component instance, because in that case that value is used and returned.) But the browser Javascript engine can't possibly know that or optimise it away.
To prove this isn't anything to do with React, I can do this:
function ignoresArgument(x) {
return 1;
}
for (let i = 0; i < 20; i ) {
ignoresArgument(
(function(y) {
console.log("I don't need to be executed, but Javascript doesn't know that so I will be executed 20 times");
return 2;
})(1)
);
}
CodePudding user response:
In addition to all the other answers, I think this link addresses your issue where you only want to call the function in useState
once.
You can change this line in the sandbox:
const [dependency] = React.useState(() => new ThirdParty());
and the third party counter would stop incrementing.
CodePudding user response:
I think your understanding of useEffect
is somewhat wrong. It is not just used for componentDidMount
. What you are describing is a symptom of a side-effect that cases state changes in the components. Here is the explanation of useEffect
from the official documentation.
If you’re familiar with React class lifecycle methods, you can think of useEffect Hook as componentDidMount, componentDidUpdate, and componentWillUnmount combined.
useEffect
would be ideal for this situation if you want to avoid re-renders that is not required.
React memo
might not be needed for your solution either.