Skip to content

PascalCase class exported via export { Name } is incorrectly treated as a React component #109

Description

@rakleed

Bug description

When a class with a PascalCase name is exported via a named export list (export { ClassName }), the plugin incorrectly treats it as a React component. This causes all other non-component exports in the same file to be flagged with the namedExport warning.

Minimal reproduction

// helpers.jsx
class TimeObject {
  constructor() {
    this.years = {};
    this.total = 0;
  }
}

const calcAmount = () => { /* ... */ };

export { calcAmount, TimeObject };
//       ^^^^^^^^^^  incorrectly flagged
//                   TimeObject is treated as a component, triggering the rule on calcAmount

Root cause

In handleExportIdentifier, when called without initParam (i.e. from an export { Name } statement rather than an inline export declaration), the plugin falls back to name-only detection:

if (!initParam) {
  if (reactComponentNameRE.test(identifierNode.name)) {
    hasReactExport = true; // PascalCase → assumed to be a component
  } else {
    nonComponentExports.push(identifierNode);
  }
  return;
}

TimeObject matches reactComponentNameRE, so hasReactExport becomes true, which causes calcAmount to be reported as a non-component export mixed with a component export.

Why the inline form works correctly

export class TimeObject {} goes through handleExportDeclarationClassDeclaration branch, which correctly checks for superClass !== null and a render method before considering it a React component. The export { } form bypasses this check entirely.

Relation to previous fixes

This is similar to #71 (PascalCase constant treated as component). The fix in #75 addressed the case of local VariableDeclaration by adding a canBeReactFunctionComponent check, but the export { Name } path (no initParam) was not updated and still relies solely on the name.

Expected behavior

export { TimeObject } where TimeObject is a class (not a function returning JSX) should be treated as a non-component export, consistent with how export class TimeObject {} is handled.

Workarounds

  • Rename the file from .jsx to .js (suppresses the rule entirely)
  • Use export class TimeObject {} inline instead of export { TimeObject }

Version

eslint-plugin-react-refresh@0.5.2

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions