New
Introducing React Doctor for Enterprise

react-doctor/no-effect-event-handler

Move the conditional logic into onClick, onChange, or onSubmit handlers directly

  • Category: State & Effects
  • Severity: warn
  • Source: oxlint-plugin-react-doctor
  • Framework: global
  • Enabled when: always

Validation prompt

Use this to decide whether a fired diagnostic is real or a false positive.

Fires when a useEffect body is a single IfStatement whose test references a prop also listed in the deps, AND the consequent contains an event-shaped side effect — fetch, post, navigate, toast, capture, track, document.classList mutations, etc. The early-return + post-if event-like statements shape also triggers. Prop identity is tracked through the per-component prop-stack. False positive: the prop change really is programmatic (e.g. URL param via routing) rather than user-driven.

Fix prompt

Use this once validation confirms the diagnostic is real.

Move the side-effect call out of the useEffect and into the JSX event handler (onClick, onChange, onSubmit) that actually triggers the prop change, so the side effect runs synchronously with the interaction. If the prop comes from a parent, lift the handler up — most 'react to a prop' effects are really 'parent needs to do X on click'. See https://react.dev/learn/you-might-not-need-an-effect#sharing-logic-between-event-handlers

More State & Effects rules from the rules reference:

  • react-doctor/no-effect-event-in-deps: Call the useEffectEvent callback inside the effect body without listing it; its identity is intentionally unstable
  • react-doctor/no-effect-with-fresh-deps: Move the constructed value into the hook body and depend on its primitive inputs, or memoize it with useMemo/useCallback so its reference is stable.
  • react-doctor/no-event-trigger-state: Delete the trigger state (`useState(null)` plus the `useEffect` that watches it) and call the side-effect (`post(...)` / `navigate(...)` / `track(...)`) directly inside the event handler that previously called the setter. State should not exist purely to schedule effect runs
  • react-doctor/no-fetch-in-effect: Use `useQuery()` from @tanstack/react-query, `useSWR()`, or fetch in a Server Component instead
  • react-doctor/no-mirror-prop-effect: Delete both the `useState` and the `useEffect` and read the prop directly during render. Mirroring a prop into local state forces a stale first render before the effect re-syncs