Home/Learn/React/You Probably Do Not Need That Effect

You Probably Do Not Need That Effect

Intermediate
State & Effects

Four patterns account for most unnecessary effects. Removing them makes code shorter, faster and correct in cases it previously got wrong.

Overview

Once useEffect is available, it becomes tempting to express everything as a reaction: when this prop changes, update that state; when the list changes, recalculate the total; when the user clicks, set a flag and let an effect do the work. Each of these produces an extra render pass, and several of them produce visible flicker or stale values. The alternatives are all simpler — compute during render, do the work in the event handler, key the component to reset it. This concept is the single biggest code-quality improvement available in a typical React codebase.

Transforming Data

The most common unnecessary effect: state derived from other state.

Derive during render, memoise only if measured
// Unnecessary — an extra render, and one frame of the wrong value
const [problems, setProblems] = useState([])
const [visible, setVisible] = useState([])
useEffect(() => {
  setVisible(problems.filter(p => p.difficulty === level))
}, [problems, level])

// Just compute it
const visible = problems.filter(p => p.difficulty === level)

// Same for totals, counts, formatted values, sorted copies.
// If it is expensive AND you have measured it, wrap in useMemo:
const visible = useMemo(
  () => problems.filter(p => p.difficulty === level),
  [problems, level],
)
// but "expensive" means thousands of items, not dozens.

Reacting to Events

If it happened because the user did something, it belongs in the handler.

Displayed -> effect. User did it -> handler.
// Unnecessary and buggy — fires again on any remount
const [submitted, setSubmitted] = useState(false)
useEffect(() => {
  if (submitted) { showToast('Saved'); track('save') }
}, [submitted])

// The event handler is the right place
async function handleSubmit(values) {
  await save(values)
  showToast('Saved')
  track('save')
}

// The distinction that decides it:
//   Did this happen because the component was DISPLAYED?  -> effect
//   Did this happen because the user DID something?       -> handler

// Chained effects are the extreme version — each one triggers the
// next, giving several render passes for one logical update:
useEffect(() => { if (card) setGoldCards(...) }, [card])
useEffect(() => { if (goldCards) setRound(...) }, [goldCards])
// Compute all of it in the handler instead.

Resetting State on a Prop Change

The effect version flickers. The key version does not.

Key to reset everything; adjust during render for part
// Common and wrong — renders the old comment once, then clears it
function CommentBox({ problemId }) {
  const [text, setText] = useState('')
  useEffect(() => { setText('') }, [problemId])   // one stale frame
}

// Reset the whole component with a key, from the parent
<CommentBox key={problemId} problemId={problemId} />
// A new key = a new instance = fresh state, with no extra render.

// Adjusting only PART of the state on a prop change is legitimate,
// and is done during render, not in an effect:
function List({ items }) {
  const [selection, setSelection] = useState(null)
  const [prevItems, setPrevItems] = useState(items)
  if (items !== prevItems) {          // during render, not an effect
    setPrevItems(items)
    setSelection(null)
  }
}
// React restarts the render immediately, before touching the DOM.

Key Points to Remember

  • 1State derived from other state should be computed during render, not synchronised with an effect
  • 2useMemo is for measured expense — filtering dozens of rows does not need it
  • 3Work caused by a user action belongs in the event handler; effects are for work caused by being displayed
  • 4Chained effects that each set the next one's state produce several render passes for one update
  • 5Reset a component's state by changing its key rather than clearing it in an effect, which renders a stale frame first

Interview Questions

Sign in to ask Aria
1

Why is setting derived state inside useEffect a problem?

Medium
2

How do you decide between doing work in an event handler and doing it in an effect?

Medium
3

What is the best way to reset a component's state when a prop changes?

Hard

Ask Aria about You Probably Do Not Need That Effect

Your personal AI tutor — ask anything about this concept

Revision Status

Personal Notes

Sign in to save personal notes for this topic.

Discussion

Sign in to join the discussion.

Loading discussion…