Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down Expand Up @@ -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.
Expand Down
6 changes: 4 additions & 2 deletions packages/eslint-plugin-react-hooks/src/rules/RulesOfHooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down