diff --git a/packages/eslint-plugin-react-hooks/__tests__/ESLintRulesOfHooks-test.js b/packages/eslint-plugin-react-hooks/__tests__/ESLintRulesOfHooks-test.js index 3d60a36824d2..6761419453f3 100644 --- a/packages/eslint-plugin-react-hooks/__tests__/ESLintRulesOfHooks-test.js +++ b/packages/eslint-plugin-react-hooks/__tests__/ESLintRulesOfHooks-test.js @@ -45,6 +45,16 @@ const allTests = { } `, }, + { + code: normalizeIndent` + // Valid because a single leading underscore is a private-naming + // convention; what follows still starts with an uppercase letter, + // so this is recognized as a component. See facebook/react#31722. + function _ComponentWithHook() { + useHook(); + } + `, + }, { syntax: 'flow', code: normalizeIndent` @@ -1114,6 +1124,17 @@ const allTests = { `, errors: [functionError('useState', 'handleClick')], }, + { + code: normalizeIndent` + // Invalid because the underscore-prefix exemption still requires + // an uppercase letter afterwards (so lowercase-after-underscore + // names are not recognized as components). + function _notAComponent() { + useHook(); + } + `, + errors: [functionError('useHook', '_notAComponent')], + }, { code: normalizeIndent` // Invalid because it's a common misunderstanding. diff --git a/packages/eslint-plugin-react-hooks/src/rules/RulesOfHooks.ts b/packages/eslint-plugin-react-hooks/src/rules/RulesOfHooks.ts index ca82c99e2f55..fb470017948f 100644 --- a/packages/eslint-plugin-react-hooks/src/rules/RulesOfHooks.ts +++ b/packages/eslint-plugin-react-hooks/src/rules/RulesOfHooks.ts @@ -52,10 +52,12 @@ function isHook(node: Node): boolean { /** * Checks if the node is a React component name. React component names must - * always start with an uppercase letter. + * always start with an uppercase letter. A single leading underscore is + * permitted as a private-naming convention (e.g. `_InternalComponent`), so + * long as it is followed by an uppercase letter. */ function isComponentName(node: Node): boolean { - return node.type === 'Identifier' && /^[A-Z]/.test(node.name); + return node.type === 'Identifier' && /^_?[A-Z]/.test(node.name); } function isReactFunction(node: Node, functionName: string): boolean {