diff --git a/packages/plugins/eslint-plugin-react-naming-convention/src/rules/use-state.md b/packages/plugins/eslint-plugin-react-naming-convention/src/rules/use-state.md index 345ef2892..447c2f1db 100644 --- a/packages/plugins/eslint-plugin-react-naming-convention/src/rules/use-state.md +++ b/packages/plugins/eslint-plugin-react-naming-convention/src/rules/use-state.md @@ -20,7 +20,12 @@ react-naming-convention/use-state ## Description -Enforces destructuring and symmetric naming of `useState` hook value and setter +Enforces destructuring and symmetric naming of `useState` hook value and setter. + +This rule ensures two things: + +1. The `useState` hook is destructured into a value and setter pair. +2. The value and setter are named symmetrically (e.g. `count` and `setCount`). ## Examples diff --git a/packages/plugins/eslint-plugin-react-naming-convention/src/rules/use-state.spec.ts b/packages/plugins/eslint-plugin-react-naming-convention/src/rules/use-state.spec.ts index 825a1bf91..ba1c86e1b 100644 --- a/packages/plugins/eslint-plugin-react-naming-convention/src/rules/use-state.spec.ts +++ b/packages/plugins/eslint-plugin-react-naming-convention/src/rules/use-state.spec.ts @@ -5,6 +5,30 @@ import rule, { RULE_NAME } from "./use-state"; ruleTester.run(RULE_NAME, rule, { invalid: [ + { + code: tsx` + function Component() { + useState(0); + + return
; + } + `, + errors: [{ + messageId: "missingDestructuring", + }], + }, + { + code: tsx` + function Component() { + const data = useState(0); + + return
; + } + `, + errors: [{ + messageId: "missingDestructuring", + }], + }, { code: tsx` function Component() { @@ -14,7 +38,7 @@ ruleTester.run(RULE_NAME, rule, { } `, errors: [{ - messageId: "invalid", + messageId: "invalidSetterNaming", }], }, { @@ -26,7 +50,7 @@ ruleTester.run(RULE_NAME, rule, { } `, errors: [{ - messageId: "invalid", + messageId: "invalidSetterNaming", }], }, { @@ -40,7 +64,7 @@ ruleTester.run(RULE_NAME, rule, { } `, errors: [{ - messageId: "invalid", + messageId: "invalidSetterNaming", }], }, { @@ -54,7 +78,7 @@ ruleTester.run(RULE_NAME, rule, { } `, errors: [{ - messageId: "invalid", + messageId: "invalidSetterNaming", }], }, { @@ -68,7 +92,7 @@ ruleTester.run(RULE_NAME, rule, { } `, errors: [{ - messageId: "invalid", + messageId: "invalidSetterNaming", }], }, { @@ -82,7 +106,7 @@ ruleTester.run(RULE_NAME, rule, { } `, errors: [{ - messageId: "invalid", + messageId: "invalidSetterNaming", }], }, { @@ -95,7 +119,7 @@ ruleTester.run(RULE_NAME, rule, { } `, errors: [{ - messageId: "invalid", + messageId: "invalidSetterNaming", }], }, ], diff --git a/packages/plugins/eslint-plugin-react-naming-convention/src/rules/use-state.ts b/packages/plugins/eslint-plugin-react-naming-convention/src/rules/use-state.ts index 1b63073e1..81f5798df 100644 --- a/packages/plugins/eslint-plugin-react-naming-convention/src/rules/use-state.ts +++ b/packages/plugins/eslint-plugin-react-naming-convention/src/rules/use-state.ts @@ -15,7 +15,7 @@ export const RULE_FEATURES = [ "CHK", ] as const satisfies RuleFeature[]; -export type MessageID = "invalid"; +export type MessageID = "missingDestructuring" | "invalidSetterNaming"; export default createRule<[], MessageID>({ meta: { @@ -25,7 +25,10 @@ export default createRule<[], MessageID>({ [Symbol.for("rule_features")]: RULE_FEATURES, }, messages: { - invalid: "An useState call is not destructured into value + setter pair.", + invalidSetterNaming: + "The setter should be named 'set' followed by the capitalized state variable name, e.g., 'setState' for 'state'.", + missingDestructuring: + "useState should be destructured into a value and setter pair, e.g., const [state, setState] = useState(...).", }, schema: [], }, @@ -38,23 +41,24 @@ export function create(context: RuleContext): RuleListener { return { "CallExpression[callee.name='useState']"(node: TSESTree.CallExpression) { if (node.parent.type !== T.VariableDeclarator) { - context.report({ messageId: "invalid", node }); + context.report({ messageId: "missingDestructuring", node }); + return; } const id = getInstanceId(node); if (id?.type !== T.ArrayPattern) { - context.report({ messageId: "invalid", node }); + context.report({ messageId: "missingDestructuring", node }); return; } const [value, setter] = id.elements; if (value == null || setter == null) { - context.report({ messageId: "invalid", node }); + context.report({ messageId: "missingDestructuring", node }); return; } const setterName = match(setter) .with({ type: T.Identifier }, (id) => id.name) .otherwise(() => _); if (setterName == null || !setterName.startsWith("set")) { - context.report({ messageId: "invalid", node }); + context.report({ messageId: "invalidSetterNaming", node }); return; } const valueName = match(value) @@ -70,7 +74,7 @@ export function create(context: RuleContext): RuleListener { }) .otherwise(() => _); if (valueName == null || `set_${valueName}` !== snakeCase(setterName)) { - context.report({ messageId: "invalid", node }); + context.report({ messageId: "invalidSetterNaming", node }); return; } },