feat(linter/react): add the rules_of_hooks rule. (#3071)

[RulesOfHooks](https://github.com/facebook/react/blob/main/packages/eslint-plugin-react-hooks/src/RulesOfHooks.js), [docs](https://react.dev/reference/rules/rules-of-hooks), [npm](https://www.npmjs.com/package/eslint-plugin-react-hooks)
This one sounds like something straight out of `Mortal Kombat`!
This commit is contained in:
rzvxa 2024-05-12 19:20:38 +00:00
parent 8519a0ab05
commit 1f135ce4ce
5 changed files with 2102 additions and 12 deletions

View file

@ -209,6 +209,7 @@ mod react {
pub mod no_unknown_property; pub mod no_unknown_property;
pub mod react_in_jsx_scope; pub mod react_in_jsx_scope;
pub mod require_render_return; pub mod require_render_return;
pub mod rules_of_hooks;
pub mod void_dom_elements_no_children; pub mod void_dom_elements_no_children;
} }
@ -639,6 +640,7 @@ oxc_macros::declare_all_lint_rules! {
react::no_is_mounted, react::no_is_mounted,
react::no_unknown_property, react::no_unknown_property,
react::require_render_return, react::require_render_return,
react::rules_of_hooks,
react::void_dom_elements_no_children, react::void_dom_elements_no_children,
react_perf::jsx_no_jsx_as_prop, react_perf::jsx_no_jsx_as_prop,
react_perf::jsx_no_new_array_as_prop, react_perf::jsx_no_new_array_as_prop,

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,596 @@
---
source: crates/oxc_linter/src/tester.rs
expression: rules_of_hooks
---
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "useState" is called conditionally. React Hooks must be called in the exact same order in every component render.
╭─[rules_of_hooks.tsx:4:15]
3 │ if (a) return;
4 │ useState();
· ──────────
5 │ }
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "useState" is called conditionally. React Hooks must be called in the exact same order in every component render.
╭─[rules_of_hooks.tsx:9:15]
8 │ }
9 │ useState();
· ──────────
10 │ }
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "useHook" is called conditionally. React Hooks must be called in the exact same order in every component render.
╭─[rules_of_hooks.tsx:7:15]
6 │
7 │ useHook();
· ─────────
8 │ }
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "useConditionalHook" is called conditionally. React Hooks must be called in the exact same order in every component render.
╭─[rules_of_hooks.tsx:4:18]
3 │ if (cond) {
4 │ useConditionalHook();
· ────────────────────
5 │ }
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "useState" cannot be called at the top level. React Hooks must be called in a React function component or a custom React Hook function.
╭─[rules_of_hooks.tsx:2:13]
1 │
2 │ Hook.useState();
· ───────────────
3 │ Hook._useState();
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "useHook" cannot be called at the top level. React Hooks must be called in a React function component or a custom React Hook function.
╭─[rules_of_hooks.tsx:5:13]
4 │ Hook.use42();
5 │ Hook.useHook();
· ──────────────
6 │ Hook.use_hook();
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "useHook" cannot be called in a class component. React Hooks must be called in a React function component or a custom React Hook function.
╭─[rules_of_hooks.tsx:4:22]
3 │ m() {
4 │ This.useHook();
· ──────────────
5 │ Super.useHook();
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "useHook" cannot be called in a class component. React Hooks must be called in a React function component or a custom React Hook function.
╭─[rules_of_hooks.tsx:5:22]
4 │ This.useHook();
5 │ Super.useHook();
· ───────────────
6 │ }
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "useFeatureFlag" cannot be called in a class component. React Hooks must be called in a React function component or a custom React Hook function.
╭─[rules_of_hooks.tsx:5:25]
4 │ if (cond) {
5 │ FooStore.useFeatureFlag();
· ─────────────────────────
6 │ }
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "useConditionalHook" is called conditionally. React Hooks must be called in the exact same order in every component render.
╭─[rules_of_hooks.tsx:4:21]
3 │ if (cond) {
4 │ Namespace.useConditionalHook();
· ──────────────────────────────
5 │ }
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "useConditionalHook" is called conditionally. React Hooks must be called in the exact same order in every component render.
╭─[rules_of_hooks.tsx:5:29]
4 │ if (cond) {
5 │ useConditionalHook();
· ────────────────────
6 │ }
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "useConditionalHook" is called conditionally. React Hooks must be called in the exact same order in every component render.
╭─[rules_of_hooks.tsx:4:25]
3 │ if (cond) {
4 │ useConditionalHook();
· ────────────────────
5 │ }
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "useConditionalHook" is called conditionally. React Hooks must be called in the exact same order in every component render.
╭─[rules_of_hooks.tsx:5:29]
4 │ if (cond) {
5 │ useConditionalHook();
· ────────────────────
6 │ }
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "useTernaryHook" is called conditionally. React Hooks must be called in the exact same order in every component render.
╭─[rules_of_hooks.tsx:3:28]
2 │ function ComponentWithTernaryHook() {
3 │ cond ? useTernaryHook() : null;
· ────────────────
4 │ }
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "useHookInsideCallback" cannot be called inside a callback. React Hooks must be called in a React function component or a custom React Hook function.
╭─[rules_of_hooks.tsx:4:25]
3 │ useEffect(() => {
4 │ useHookInsideCallback();
· ───────────────────────
5 │ });
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "useHookInsideCallback" cannot be called inside a callback. React Hooks must be called in a React function component or a custom React Hook function.
╭─[rules_of_hooks.tsx:5:29]
4 │ useEffect(() => {
5 │ useHookInsideCallback();
· ───────────────────────
6 │ });
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "useHookInsideCallback" cannot be called inside a callback. React Hooks must be called in a React function component or a custom React Hook function.
╭─[rules_of_hooks.tsx:4:25]
3 │ useEffect(() => {
4 │ useHookInsideCallback();
· ───────────────────────
5 │ });
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "useHookInsideCallback" cannot be called inside a callback. React Hooks must be called in a React function component or a custom React Hook function.
╭─[rules_of_hooks.tsx:4:25]
3 │ useEffect(() => {
4 │ useHookInsideCallback();
· ───────────────────────
5 │ });
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "useState" is called in function "handleClick" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use".
╭─[rules_of_hooks.tsx:3:30]
2 │ function ComponentWithHookInsideCallback() {
3 │ function handleClick() {
· ───────────
4 │ useState();
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "useState" is called in function "handleClick" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use".
╭─[rules_of_hooks.tsx:4:34]
3 │ return function ComponentWithHookInsideCallback() {
4 │ function handleClick() {
· ───────────
5 │ useState();
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "useHookInsideLoop" may be executed more than once. Possibly because it is called in a loop. React Hooks must be called in the exact same order in every component render.
╭─[rules_of_hooks.tsx:4:25]
3 │ while (cond) {
4 │ useHookInsideLoop();
· ───────────────────
5 │ }
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "useState" is called in function "renderItem" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use".
╭─[rules_of_hooks.tsx:2:26]
1 │
2 │ function renderItem() {
· ──────────
3 │ useState();
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "useHookInsideNormalFunction" is called in function "normalFunctionWithHook" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use".
╭─[rules_of_hooks.tsx:2:26]
1 │
2 │ function normalFunctionWithHook() {
· ──────────────────────
3 │ useHookInsideNormalFunction();
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "useHookInsideNormalFunction" is called in function "_normalFunctionWithHook" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use".
╭─[rules_of_hooks.tsx:2:26]
1 │
2 │ function _normalFunctionWithHook() {
· ───────────────────────
3 │ useHookInsideNormalFunction();
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "useHookInsideNormalFunction" is called in function "_useNotAHook" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use".
╭─[rules_of_hooks.tsx:5:26]
4 │ }
5 │ function _useNotAHook() {
· ────────────
6 │ useHookInsideNormalFunction();
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "useHookInsideNormalFunction" is called in function "normalFunctionWithConditionalHook" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use".
╭─[rules_of_hooks.tsx:2:26]
1 │
2 │ function normalFunctionWithConditionalHook() {
· ─────────────────────────────────
3 │ if (cond) {
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "useHook1" may be executed more than once. Possibly because it is called in a loop. React Hooks must be called in the exact same order in every component render.
╭─[rules_of_hooks.tsx:4:25]
3 │ while (a) {
4 │ useHook1();
· ──────────
5 │ if (b) return;
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "useHook2" is called conditionally. React Hooks must be called in the exact same order in every component render.
╭─[rules_of_hooks.tsx:6:25]
5 │ if (b) return;
6 │ useHook2();
· ──────────
7 │ }
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "useHook3" may be executed more than once. Possibly because it is called in a loop. React Hooks must be called in the exact same order in every component render.
╭─[rules_of_hooks.tsx:9:25]
8 │ while (c) {
9 │ useHook3();
· ──────────
10 │ if (d) return;
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "useHook4" is called conditionally. React Hooks must be called in the exact same order in every component render.
╭─[rules_of_hooks.tsx:11:25]
10 │ if (d) return;
11 │ useHook4();
· ──────────
12 │ }
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "useHook1" may be executed more than once. Possibly because it is called in a loop. React Hooks must be called in the exact same order in every component render.
╭─[rules_of_hooks.tsx:4:21]
3 │ while (a) {
4 │ useHook1();
· ──────────
5 │ if (b) continue;
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "useHook2" is called conditionally. React Hooks must be called in the exact same order in every component render.
╭─[rules_of_hooks.tsx:6:21]
5 │ if (b) continue;
6 │ useHook2();
· ──────────
7 │ }
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "useHook" is called conditionally. React Hooks must be called in the exact same order in every component render.
╭─[rules_of_hooks.tsx:5:25]
4 │ if (a) break label;
5 │ useHook();
· ─────────
6 │ }
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "useState" is called in function "a" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use".
╭─[rules_of_hooks.tsx:2:22]
1 │
2 │ function a() { useState(); }
· ─
3 │ const whatever = function b() { useState(); };
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "useState" is called in function "b" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use".
╭─[rules_of_hooks.tsx:3:39]
2 │ function a() { useState(); }
3 │ const whatever = function b() { useState(); };
· ─
4 │ const c = () => { useState(); };
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "useState" is called in function "Anonymous" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use".
╭─[rules_of_hooks.tsx:4:23]
3 │ const whatever = function b() { useState(); };
4 │ const c = () => { useState(); };
· ─────────────────────
5 │ let d = () => useState();
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "useState" is called in function "Anonymous" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use".
╭─[rules_of_hooks.tsx:5:21]
4 │ const c = () => { useState(); };
5 │ let d = () => useState();
· ────────────────
6 │ e = () => { useState(); };
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "useState" is called conditionally. React Hooks must be called in the exact same order in every component render.
╭─[rules_of_hooks.tsx:4:21]
3 │ if (a) return;
4 │ useState();
· ──────────
5 │ }
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "useState" is called conditionally. React Hooks must be called in the exact same order in every component render.
╭─[rules_of_hooks.tsx:9:21]
8 │ }
9 │ useState();
· ──────────
10 │ }
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "useState" is called conditionally. React Hooks must be called in the exact same order in every component render.
╭─[rules_of_hooks.tsx:9:21]
8 │ if (a) return;
9 │ useState();
· ──────────
10 │ }
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "useHook1" is called conditionally. React Hooks must be called in the exact same order in every component render.
╭─[rules_of_hooks.tsx:3:26]
2 │ function useHook() {
3 │ a && useHook1();
· ──────────
4 │ b && useHook2();
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "useHook2" is called conditionally. React Hooks must be called in the exact same order in every component render.
╭─[rules_of_hooks.tsx:4:26]
3 │ a && useHook1();
4 │ b && useHook2();
· ──────────
5 │ }
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "useState" is called conditionally. React Hooks must be called in the exact same order in every component render.
╭─[rules_of_hooks.tsx:5:25]
4 │ f();
5 │ useState();
· ──────────
6 │ } catch {}
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "useState" is called conditionally. React Hooks must be called in the exact same order in every component render.
╭─[rules_of_hooks.tsx:3:39]
2 │ function useHook({ bar }) {
3 │ let foo1 = bar && useState();
· ──────────
4 │ let foo2 = bar || useState();
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "useState" is called conditionally. React Hooks must be called in the exact same order in every component render.
╭─[rules_of_hooks.tsx:4:39]
3 │ let foo1 = bar && useState();
4 │ let foo2 = bar || useState();
· ──────────
5 │ let foo3 = bar ?? useState();
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "useState" is called conditionally. React Hooks must be called in the exact same order in every component render.
╭─[rules_of_hooks.tsx:5:39]
4 │ let foo2 = bar || useState();
5 │ let foo3 = bar ?? useState();
· ──────────
6 │ }
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "useCustomHook" is called conditionally. React Hooks must be called in the exact same order in every component render.
╭─[rules_of_hooks.tsx:4:25]
3 │ if (props.fancy) {
4 │ useCustomHook();
· ───────────────
5 │ }
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "useCustomHook" is called conditionally. React Hooks must be called in the exact same order in every component render.
╭─[rules_of_hooks.tsx:4:25]
3 │ if (props.fancy) {
4 │ useCustomHook();
· ───────────────
5 │ }
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "useCustomHook" is called conditionally. React Hooks must be called in the exact same order in every component render.
╭─[rules_of_hooks.tsx:4:25]
3 │ if (props.fancy) {
4 │ useCustomHook();
· ───────────────
5 │ }
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "useProbablyAHook" is called in function "notAComponent" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use".
╭─[rules_of_hooks.tsx:2:48]
1 │
2 │ React.unknownFunction(function notAComponent(foo, bar) {
· ─────────────
3 │ useProbablyAHook(bar)
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "useState" cannot be called at the top level. React Hooks must be called in a React function component or a custom React Hook function.
╭─[rules_of_hooks.tsx:2:13]
1 │
2 │ useState();
· ──────────
3 │ if (foo) {
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "useCallback" cannot be called at the top level. React Hooks must be called in a React function component or a custom React Hook function.
╭─[rules_of_hooks.tsx:4:29]
3 │ if (foo) {
4 │ const foo = React.useCallback(() => {});
· ───────────────────────────
5 │ }
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "useCustomHook" cannot be called at the top level. React Hooks must be called in a React function component or a custom React Hook function.
╭─[rules_of_hooks.tsx:6:13]
5 │ }
6 │ useCustomHook();
· ───────────────
7 │
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "useBasename" cannot be called at the top level. React Hooks must be called in a React function component or a custom React Hook function.
╭─[rules_of_hooks.tsx:3:36]
2 │ const {createHistory, useBasename} = require('history-2.1.2');
3 │ const browserHistory = useBasename(createHistory)({
· ──────────────────────────
4 │ basename: '/',
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "useFeatureFlag" cannot be called in a class component. React Hooks must be called in a React function component or a custom React Hook function.
╭─[rules_of_hooks.tsx:5:29]
4 │ if (foo) {
5 │ useFeatureFlag();
· ────────────────
6 │ }
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "useState" cannot be called in a class component. React Hooks must be called in a React function component or a custom React Hook function.
╭─[rules_of_hooks.tsx:4:25]
3 │ render() {
4 │ React.useState();
· ────────────────
5 │ }
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "useState" cannot be called in a class component. React Hooks must be called in a React function component or a custom React Hook function.
╭─[rules_of_hooks.tsx:1:27]
1 │ (class {useHook = () => { useState(); }});
· ──────────
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "useState" cannot be called in a class component. React Hooks must be called in a React function component or a custom React Hook function.
╭─[rules_of_hooks.tsx:1:21]
1 │ (class {useHook() { useState(); }});
· ──────────
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "useState" cannot be called in a class component. React Hooks must be called in a React function component or a custom React Hook function.
╭─[rules_of_hooks.tsx:1:21]
1 │ (class {h = () => { useState(); }});
· ──────────
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "useState" cannot be called in a class component. React Hooks must be called in a React function component or a custom React Hook function.
╭─[rules_of_hooks.tsx:1:15]
1 │ (class {i() { useState(); }});
· ──────────
╰────
× eslint-plugin-react-hooks(rules-of-hooks): message: `React Hook "AsyncComponent" cannot be called in an async function.
╭─[rules_of_hooks.tsx:2:32]
1 │
2 │ async function AsyncComponent() {
· ──────────────
3 │ useState();
╰────
× eslint-plugin-react-hooks(rules-of-hooks): message: `React Hook "Anonymous" cannot be called in an async function.
╭─[rules_of_hooks.tsx:2:40]
1 │
2 │ ╭─▶ const AsyncComponent = async () => {
3 │ │ useState();
4 │ ╰─▶ }
5 │
╰────
× eslint-plugin-react-hooks(rules-of-hooks): message: `React Hook "useAsyncHook" cannot be called in an async function.
╭─[rules_of_hooks.tsx:2:32]
1 │
2 │ async function useAsyncHook() {
· ────────────
3 │ useState();
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "use" cannot be called at the top level. React Hooks must be called in a React function component or a custom React Hook function.
╭─[rules_of_hooks.tsx:2:13]
1 │
2 │ Hook.use();
· ──────────
3 │ Hook._use();
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "useState" cannot be called at the top level. React Hooks must be called in a React function component or a custom React Hook function.
╭─[rules_of_hooks.tsx:4:13]
3 │ Hook._use();
4 │ Hook.useState();
· ───────────────
5 │ Hook._useState();
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "useHook" cannot be called at the top level. React Hooks must be called in a React function component or a custom React Hook function.
╭─[rules_of_hooks.tsx:7:13]
6 │ Hook.use42();
7 │ Hook.useHook();
· ──────────────
8 │ Hook.use_hook();
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "use" is called in function "notAComponent" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use".
╭─[rules_of_hooks.tsx:2:26]
1 │
2 │ function notAComponent() {
· ─────────────
3 │ use(promise);
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "use" cannot be called at the top level. React Hooks must be called in a React function component or a custom React Hook function.
╭─[rules_of_hooks.tsx:2:26]
1 │
2 │ const text = use(promise);
· ────────────
3 │ function App() {
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "use" cannot be called in a class component. React Hooks must be called in a React function component or a custom React Hook function.
╭─[rules_of_hooks.tsx:4:21]
3 │ m() {
4 │ use(promise);
· ────────────
5 │ }
╰────
× eslint-plugin-react-hooks(rules-of-hooks): message: `React Hook "AsyncComponent" cannot be called in an async function.
╭─[rules_of_hooks.tsx:2:28]
1 │
2 │ async function AsyncComponent() {
· ──────────────
3 │ use();
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "useState" is called in function "Anonymous" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use".
╭─[rules_of_hooks.tsx:3:24]
2 │ export const notAComponent = () => {
3 │ ╭─▶ return () => {
4 │ │ useState();
5 │ ╰─▶ }
6 │ }
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "useState" is called in function "Anonymous" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use".
╭─[rules_of_hooks.tsx:2:35]
1 │
2 │ ╭─▶ const notAComponent = () => {
3 │ │ useState();
4 │ ╰─▶ }
5 │
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "useState" is called in function "Anonymous" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use".
╭─[rules_of_hooks.tsx:2:28]
1 │
2 │ ╭─▶ export default () => {
3 │ │ if (isVal) {
4 │ │ useState(0);
5 │ │ }
6 │ ╰─▶ }
7 │
╰────
× eslint-plugin-react-hooks(rules-of-hooks): React Hook "useState" is called in function "Anonymous" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use".
╭─[rules_of_hooks.tsx:2:28]
1 │
2 │ ╭─▶ export default function() {
3 │ │ if (isVal) {
4 │ │ useState(0);
5 │ │ }
6 │ ╰─▶ }
7 │
╰────

View file

@ -1,9 +1,9 @@
use oxc_ast::{ use oxc_ast::{
ast::{ ast::{
CallExpression, Expression, JSXAttributeItem, JSXAttributeName, JSXAttributeValue, CallExpression, Expression, JSXAttributeItem, JSXAttributeName, JSXAttributeValue,
JSXChild, JSXElement, JSXElementName, JSXExpression, JSXOpeningElement, JSXChild, JSXElement, JSXElementName, JSXExpression, JSXOpeningElement, MemberExpression,
}, },
AstKind, match_member_expression, AstKind,
}; };
use oxc_semantic::{AstNode, SymbolFlags}; use oxc_semantic::{AstNode, SymbolFlags};
@ -273,3 +273,56 @@ pub fn parse_jsx_value(value: &JSXAttributeValue) -> Result<f64, ()> {
_ => Err(()), _ => Err(()),
} }
} }
/// Checks whether the `name` follows the official conventions of React Hooks.
///
/// Identifies `use(...)` as a valid hook.
///
/// Hook names must start with use followed by a capital letter,
/// like useState (built-in) or useOnlineStatus (custom).
pub fn is_react_hook_name(name: &str) -> bool {
name.starts_with("use") && name.chars().nth(3).map_or(true, char::is_uppercase)
// uncomment this check if react decided to drop the idea of `use` hook.
// <https://react.dev/reference/react/use> It is currently in `Canary` builds.
// name.starts_with("use") && name.chars().nth(3).is_some_and(char::is_uppercase)
}
/// Checks whether the `name` follows the official conventions of React Hooks.
///
/// Identifies `use(...)` as a valid hook.
///
/// Hook names must start with use followed by a capital letter,
/// like useState (built-in) or useOnlineStatus (custom).
pub fn is_react_hook(expr: &Expression) -> bool {
match expr {
match_member_expression!(Expression) => {
// SAFETY: We already have checked that `expr` is a member expression using the
// `match_member_expression` macro.
#[allow(unsafe_code)]
let expr = unsafe { expr.as_member_expression().unwrap_unchecked() };
let MemberExpression::StaticMemberExpression(static_expr) = expr else { return false };
let is_valid_namespace = match &static_expr.object {
Expression::Identifier(ident) => {
ident.name.chars().next().is_some_and(char::is_uppercase)
}
Expression::ThisExpression(_) | Expression::Super(_) => false,
_ => true,
};
is_valid_namespace && expr.static_property_name().is_some_and(is_react_hook_name)
}
Expression::Identifier(ident) => is_react_hook_name(ident.name.as_str()),
_ => false,
}
}
/// Checks if the node is a React component name. React component names must
/// always start with an uppercase letter.
pub fn is_react_component_name(name: &str) -> bool {
name.chars().next().is_some_and(|c| c.is_ascii_uppercase())
}
/// Checks if the node is a React component name or React hook,
/// `is_react_component_name`, `is_react_hook_name`
pub fn is_react_component_or_hook_name(name: &str) -> bool {
is_react_component_name(name) || is_react_hook_name(name)
}

View file

@ -1,13 +1,7 @@
use petgraph::{stable_graph::NodeIndex, visit::EdgeRef, Direction, Graph}; use petgraph::{stable_graph::NodeIndex, visit::EdgeRef, Direction, Graph};
/// # Panics /// # Panics
pub fn neighbors_filtered_by_edge_weight< pub fn neighbors_filtered_by_edge_weight<State: Default + Clone, NodeWeight, EdgeWeight, F, G>(
State: Default + Copy + Clone,
NodeWeight,
EdgeWeight,
F,
G,
>(
graph: &Graph<NodeWeight, EdgeWeight>, graph: &Graph<NodeWeight, EdgeWeight>,
node: NodeIndex, node: NodeIndex,
edge_filter: &F, edge_filter: &F,
@ -37,11 +31,11 @@ where
} else { } else {
let opposite_dir_of_edge_graph_ix = edge.target(); let opposite_dir_of_edge_graph_ix = edge.target();
let (new_state, keep_walking_this_path) = let (new_state, keep_walking_this_path) =
visitor(&opposite_dir_of_edge_graph_ix, state); visitor(&opposite_dir_of_edge_graph_ix, state.clone());
if keep_walking_this_path { if keep_walking_this_path {
q.push((opposite_dir_of_edge_graph_ix, new_state)); q.push((opposite_dir_of_edge_graph_ix, new_state.clone()));
} else { } else {
final_states.push(new_state); final_states.push(new_state.clone());
} }
edges += 1; edges += 1;
} }