New
Introducing React Doctor for Enterprise

react-doctor/no-will-update-set-state

Don't call this.setState in componentWillUpdate — move the update to getDerivedStateFromProps or componentDidUpdate.

Validation prompt

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

Fires on a this.setState(...) call located directly in a componentWillUpdate (or UNSAFE_componentWillUpdate when React >= 16.3) method of a createReactClass or extends React.Component component; calls inside an if/loop block still count as direct. In the default "allowed" mode, calls buried two or more functions deep (event-handler callbacks, inner function declarations passed elsewhere) are NOT flagged — only the stricter "disallow-in-func" mode catches those. False positive: a project pinned to React < 16.3 where a method literally named UNSAFE_componentWillUpdate is just an ordinary user method, not a lifecycle hook — set settings.react.version so the rule skips it.

Fix prompt

Use this once validation confirms the diagnostic is real.

Move the derivation out of componentWillUpdate: compute prop-derived state in the static getDerivedStateFromProps(props, state) (return the partial state instead of calling setState), or run post-render side effects in componentDidUpdate(prevProps, prevState) guarded by a prop/state comparison so it doesn't loop. componentWillUpdate is not re-invoked by setState, so the update silently does nothing or recurses — e.g. replace componentWillUpdate() { this.setState({name: this.props.name}) } with static getDerivedStateFromProps(props) { return { name: props.name }; }. See https://oxc.rs/docs/guide/usage/linter/rules/react/no-will-update-set-state

More State & Effects rules from the rules reference:

  • react-doctor/prefer-use: Replace useContext(Context) with the React 19 use(Context) API, which reads the same value but may be called conditionally
  • react-doctor/prefer-use-effect-event: Wrap the callback with `useEffectEvent(callback)` (React 19+) and call the resulting binding from inside the sub-handler. The Effect Event captures the latest props/state without being a reactive dep, so the effect doesn't re-subscribe on every parent render. See https://react.dev/reference/react/useEffectEvent
  • react-doctor/prefer-use-sync-external-store: Replace the `useState(getSnapshot())` + `useEffect(() => store.subscribe(() => setSnapshot(getSnapshot())))` pair with `useSyncExternalStore(store.subscribe, getSnapshot)`. The hook handles tearing during concurrent renders and SSR snapshots; the manual subscribe pattern doesn't
  • react-doctor/prefer-useReducer: Group related state: `const [state, dispatch] = useReducer(reducer, { field1, field2, ... })`
  • react-doctor/rerender-dependencies: Extract to a useMemo, useRef, or module-level constant so the reference is stable