diff --git a/packages/plugins/eslint-plugin-react-hooks-extra/src/rules/prefer-use-state-lazy-initialization.spec.ts b/packages/plugins/eslint-plugin-react-hooks-extra/src/rules/prefer-use-state-lazy-initialization.spec.ts index 775088c2d..6b445c69c 100644 --- a/packages/plugins/eslint-plugin-react-hooks-extra/src/rules/prefer-use-state-lazy-initialization.spec.ts +++ b/packages/plugins/eslint-plugin-react-hooks-extra/src/rules/prefer-use-state-lazy-initialization.spec.ts @@ -260,6 +260,27 @@ ruleTester.run(RULE_NAME, rule, { 'const { useState } = require("react"); useState(1 < 2 ? 3 : 4)', 'const { useState } = require("react"); useState(1 == 2 ? 3 : 4)', 'const { useState } = require("react"); useState(1 === 2 ? 3 : 4)', + "const [state, setState] = useState(use(promise));", + { + code: tsx` + import { useState, use } from 'react'; + + const promise = Promise.resolve(); + + function App() { + const [state, setState] = useState(use(promise)); + + return null; + } + + export default App; + `, + settings: { + "react-x": { + version: "19.0.0", + }, + }, + }, { code: "useLocalStorageState()", settings: { diff --git a/packages/plugins/eslint-plugin-react-hooks-extra/src/rules/prefer-use-state-lazy-initialization.ts b/packages/plugins/eslint-plugin-react-hooks-extra/src/rules/prefer-use-state-lazy-initialization.ts index e5c8cfcd1..93d924b80 100644 --- a/packages/plugins/eslint-plugin-react-hooks-extra/src/rules/prefer-use-state-lazy-initialization.ts +++ b/packages/plugins/eslint-plugin-react-hooks-extra/src/rules/prefer-use-state-lazy-initialization.ts @@ -1,6 +1,6 @@ // Ported from https://github.com/jsx-eslint/eslint-plugin-react/pull/3579/commits/ebb739a0fe99a2ee77055870bfda9f67a2691374 import * as AST from "@eslint-react/ast"; -import { isReactHookCall, isReactHookCallWithNameLoose, isUseStateCall } from "@eslint-react/core"; +import { isReactHookCall, isReactHookCallWithNameLoose, isReactHookName, isUseStateCall } from "@eslint-react/core"; import type { RuleContext, RuleFeature } from "@eslint-react/shared"; import { getSettingsFromContext } from "@eslint-react/shared"; import type { RuleListener } from "@typescript-eslint/utils/ts-eslint"; @@ -14,8 +14,16 @@ export const RULE_FEATURES = [] as const satisfies RuleFeature[]; export type MessageID = CamelCase; -// variables should be defined here -const ALLOW_LIST = ["Boolean", "String", "Number"]; +// identifier names for allowed function names +const ALLOW_LIST = [ + "Boolean", + "String", + "Number", +]; + +function isAllowedName(name: string): boolean { + return ALLOW_LIST.includes(name) || isReactHookName(name); +} // rule takes inspiration from https://github.com/facebook/react/issues/26520 export default createRule<[], MessageID>({ @@ -54,11 +62,11 @@ export function create(context: RuleContext): RuleListener { const nestedCallExpressions = AST.getNestedCallExpressions(useStateInput); const hasFunctionCall = nestedCallExpressions.some((n) => { return "name" in n.callee - && !ALLOW_LIST.includes(n.callee.name); + && !isAllowedName(n.callee.name); }); const hasNewCall = AST.getNestedNewExpressions(useStateInput).some((n) => { return "name" in n.callee - && !ALLOW_LIST.includes(n.callee.name); + && !isAllowedName(n.callee.name); }); if (!hasFunctionCall && !hasNewCall) { return;