jint

Use case: always name React effects

A convention a syntax check cannot enforce: every useEffect gets a function named for what the effect does. useEffect(function syncDocumentTitle() {...}) reads better than an anonymous arrow, and a name that cannot be written is a sign the effect does too much. Whether a name says what the body does is a judgment, so it is a rule. The result ships as is_effect_named in jint:rules/react/hooks.

import { is_effect_named } from "jint:rules/react/hooks";

export default { rules: { use: [is_effect_named] } };

The question

The rule asks one thing: does every effect hook call in this function pass a named function whose name says what the effect does? The criteria pin both answers.

  • Yes: the function calls no effect hook, or every effect hook gets a named function, or a reference to one, whose name states the purpose (syncDocumentTitle, subscribeToResize).
  • No: an effect gets an anonymous arrow, a name that states no purpose (effect, onMount), or a name for a different job than the body does. An effect that does two things is not this defect: the rule judges the name, not the design.

A function with no effect hook reads yes, so it never fires. A high reading is good, so the handle warns below 0.6 and errors below 0.3.

The controls

A control is a small file whose right answer you know. Seven cover the question:

control reading
named function expressions 1.00
a named function passed by reference 1.00
a component with no effect 1.00
two jobs in one name (loadDashboardAndTrackView) 0.96
anonymous arrows 0.00
generic names (effect, onMount) 0.00
a name for the wrong job (fetchUser on a resize) 0.04

The defective controls read at most 0.04 and the healthy ones at least 0.96, and the thresholds sit inside that gap. The two-jobs control is deliberate: the rule reads it as named well, because the name states a purpose.

A real codebase

Controls prove the question separates cases you wrote. A real codebase shows whether it fires where you mean. On one React codebase’s components directory, 543 functions were measured and 20 of them call an effect hook. All 20 read below 0.3, and no function without an effect fired. That codebase has 75 effect calls and none passes a named function expression, so the rule fires on every component that has one. If your convention is that effects are named, that is the rule working.

What to know before you use it

  • The unit is the function. An effect call inside a component is not an element of its own, so a component with three effects gets one reading. One named effect and two anonymous ones read as a single middle number.
  • TSX only. The rule’s own include is **/*.tsx, the files the calibration covered.
  • The path stays out. A rule sees only the source unless it sets includePath. This rule does not, so the same component reads the same wherever the file sits.

To change what the rule judges, copy it into your config and edit the question, as Calibration describes.