Skip to content

Commit d90a60d

Browse files
committed
fix
1 parent b2d0b64 commit d90a60d

File tree

3 files changed

+23
-7
lines changed

3 files changed

+23
-7
lines changed

packages/react-doctor/src/plugin/rules/state-and-effects.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ export const noDerivedStateEffect: Rule = {
5050
if (!containsOnlySetStateCalls) return;
5151

5252
let allArgumentsDeriveFromDeps = true;
53+
let hasAnyDependencyReference = false;
5354
for (const statement of statements) {
5455
const setStateArguments = statement.expression.arguments;
5556
if (!setStateArguments?.length) continue;
@@ -59,11 +60,15 @@ export const noDerivedStateEffect: Rule = {
5960
if (child.type === "Identifier") referencedIdentifiers.push(child.name);
6061
});
6162

62-
if (
63-
referencedIdentifiers.some(
64-
(name) => !dependencyNames.has(name) && !isSetterIdentifier(name),
65-
)
66-
) {
63+
const nonSetterIdentifiers = referencedIdentifiers.filter(
64+
(name) => !isSetterIdentifier(name),
65+
);
66+
67+
if (nonSetterIdentifiers.some((name) => dependencyNames.has(name))) {
68+
hasAnyDependencyReference = true;
69+
}
70+
71+
if (nonSetterIdentifiers.some((name) => !dependencyNames.has(name))) {
6772
allArgumentsDeriveFromDeps = false;
6873
break;
6974
}
@@ -72,7 +77,9 @@ export const noDerivedStateEffect: Rule = {
7277
if (allArgumentsDeriveFromDeps) {
7378
context.report({
7479
node,
75-
message: "Derived state in useEffect — compute during render instead",
80+
message: hasAnyDependencyReference
81+
? "Derived state in useEffect — compute during render instead"
82+
: "State reset in useEffect — use a key prop to reset component state when props change",
7683
});
7784
}
7885
},

packages/react-doctor/src/utils/run-oxlint.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ const RULE_CATEGORY_MAP: Record<string, string> = {
8787

8888
const RULE_HELP_MAP: Record<string, string> = {
8989
"no-derived-state-effect":
90-
"Compute during render: `const derived = computeFrom(dep1, dep2)` — no useEffect needed",
90+
"For derived state, compute inline: `const x = fn(dep)`. For state resets on prop change, use a key prop: `<Component key={prop} />`",
9191
"no-fetch-in-effect":
9292
"Use `useQuery()` from @tanstack/react-query, `useSWR()`, or fetch in a Server Component instead",
9393
"no-cascading-set-state":

packages/react-doctor/tests/fixtures/basic-react/src/state-issues.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,14 @@ const DerivedStateComponent = ({ items }: { items: string[] }) => {
1010
return <div>{filteredItems.join(",")}</div>;
1111
};
1212

13+
const StateResetComponent = ({ visible }: { visible: boolean }) => {
14+
const [inputValue, setInputValue] = useState("");
15+
useEffect(() => {
16+
setInputValue("");
17+
}, [visible]);
18+
return <input value={inputValue} onChange={(event) => setInputValue(event.target.value)} />;
19+
};
20+
1321
const FetchInEffectComponent = () => {
1422
const [data, setData] = useState(null);
1523

@@ -91,6 +99,7 @@ const DependencyLiteralComponent = () => {
9199

92100
export {
93101
DerivedStateComponent,
102+
StateResetComponent,
94103
FetchInEffectComponent,
95104
LazyInitComponent,
96105
CascadingSetStateComponent,

0 commit comments

Comments
 (0)