Component Lifecycle

Med

Functional components have a lifecycle managed through hooks. A component mounts when it first appears in the tree, updates when its state or props change, and unmounts when removed. useEffect's setup and cleanup functions map to these phases, replacing class lifecycle methods.

Interactive Visualization

Key Points

  • Mount: component appears in the tree, initial render occurs
  • Update: re-render triggered by state change, prop change, or parent re-render
  • Unmount: component removed from the tree, cleanup runs
  • useEffect setup runs after mount and after every update matching dependencies
  • useEffect cleanup runs before the next effect and on unmount
  • StrictMode double-invokes effects in development to detect impure code
  • Think in terms of synchronization, not lifecycle events

Code Examples

Lifecycle with useEffect

import { useState, useEffect } from 'react'

function ChatRoom({ roomId }: { roomId: string }) {
  const [messages, setMessages] = useState<string[]>([])

  useEffect(() => {
    // Setup: runs on mount and when roomId changes
    const connection = createConnection(roomId)
    connection.on('message', (msg: string) => {
      setMessages((prev) => [...prev, msg])
    })

    // Cleanup: runs before re-connecting and on unmount
    return () => {
      connection.disconnect()
    }
  }, [roomId])

  return (
    <ul>
      {messages.map((msg, i) => <li key={i}>{msg}</li>)}
    </ul>
  )
}

function createConnection(roomId: string) {
  return {
    on: (_event: string, _cb: (msg: string) => void) => {},
    disconnect: () => {},
  }
}

When roomId changes, cleanup disconnects from the old room before setup connects to the new room, preventing leaked connections

Mount-Only and Unmount Logic

import { useEffect, useRef } from 'react'

function AnimatedComponent() {
  const elementRef = useRef<HTMLDivElement>(null)

  useEffect(() => {
    // Mount: initialize animation library
    const animation = startAnimation(elementRef.current)

    return () => {
      // Unmount: clean up animation
      animation.destroy()
    }
  }, []) // Empty deps: mount + unmount only

  return <div ref={elementRef}>Animated content</div>
}

function startAnimation(_el: HTMLDivElement | null) {
  return { destroy: () => {} }
}

An empty dependency array makes the effect run only on mount with cleanup only on unmount, similar to componentDidMount and componentWillUnmount

Common Mistakes

  • Thinking in lifecycle methods instead of effect synchronization
  • Not understanding that effects run after render, not during
  • Missing cleanup for subscriptions causing memory leaks after unmount

Interview Tips

  • Map class lifecycle methods to hooks: componentDidMount -> useEffect with [], componentDidUpdate -> useEffect with deps
  • Explain the shift from lifecycle thinking to synchronization thinking
  • Discuss how StrictMode double-mounting helps catch missing cleanups

Related Concepts