Do you want to request a feature or report a bug?
BUG (possibly)
What is the current behavior?
The plugin is showing this error:
React Hook "useState" is called conditionally. React Hooks must be called in the exact same order in every component render. Did you accidentally call a React Hook after an early return? (react-hooks/rules-of-hooks)eslint
But I don't think I'm calling any hooks conditionally.
The code:
https://codesandbox.io/s/exciting-bhabha-mqj7q
function App(props) {
const someObject = { propA: true, propB: false };
for (const propName in someObject) {
if (propName === true) {
console.log("something");
} else {
console.log("whatever");
}
}
// THE PLUGIN ERROR MSG ON THIS useState
const [myState, setMyState] = useState(null);
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
</div>
);
}
What is the expected behavior?
The plugin wouldn't show the error in this situation.
Which versions of React, and which browser / OS are affected by this issue? Did this work in previous versions of React?
I noticed that for this particular example:
pathsFromStartToEnd
is 3
allPathsFromStartToEnd
is 2
also possiblyHasEarlyReturn
is true
This particular error is reported when pathsFromStartToEnd
and allPathsFromStartToEnd
are unequal.
See RulesOfHooks.js line 404
If you abstract the code inside of the for..in
loop into a separate function, the lint error goes away.
In this case, pathsFromStartToEnd
and allPathsFromStartToEnd
both equal 2
:
const someObject = { propA: true, propB: false };
const someFunction = (propName) => {
if (propName === true) {
console.log("something");
} else {
console.log("whatever");
}
}
for (const propName in someObject) {
someFunction(propName)
}
const [myState, setMyState] = useState(null);
I'm still not sure why.
@gaearon, I created an MR addressing and fixing this issue (#16853).
The main reason this happens is that the value of allPathsFromStartToEnd
is calculated wrong.
In this example, the number of paths from start to end should be 3, but the function returns 2.
The reason this happens is because of flawed caching of cyclic paths. So in normal graph traversing, we need the path history as well, but it only takes a list of cyclic elements, and the elements leading to this path are not known.
As a result, when a condition is used in a for...in obj
, based on the direction we might lose some paths.
This is still an issue with 2.4.0, a little bit simpler repro:
import * as React from "react";
function Component() {
let isLastEven = false;
for (let x of [1, 2, 3]) {
if (x % 2 == 0) {
isLastEven = true;
} else {
isLastEven = false;
}
}
let y = React.useMemo(() => 1, []);
return <div>{y}</div>;
}
can #16853 be re-openned?
Most helpful comment
Merged https://github.com/facebook/react/pull/16853.