React 生命週期方法 (Lifecycle Methods)

生命週期方法是 Class Component 的概念。在 Function Component 中,我們使用 useEffect 來處理類似的邏輯。本篇作為舊版語法的補充參考。

React Class Component 有一系列的生命週期方法,讓你可以在元件的不同階段執行程式碼。

生命週期階段

元件的生命週期分為三個階段:

  1. Mounting(掛載):元件被建立並插入 DOM
  2. Updating(更新):props 或 state 改變,觸發重新渲染
  3. Unmounting(卸載):元件從 DOM 中移除

Mounting 階段

當元件被建立並插入 DOM 時,會依序執行:

constructor(props)

constructor(props) {
  super(props);  // 必須呼叫
  this.state = { count: 0 };  // 初始化 state
  this.handleClick = this.handleClick.bind(this);  // 綁定方法
}

用途:

  • 初始化 state
  • 綁定事件處理方法

render()

render() {
  return <div>{this.state.count}</div>;
}

render() 是唯一必須實作的方法,用於描述 UI。

注意:不要在 render() 中修改 state 或執行副作用。

componentDidMount()

componentDidMount() {
  // 元件已經掛載到 DOM
  this.fetchData();
  this.subscription = subscribeToData();
}

用途:

  • 發送 API 請求
  • 設定訂閱
  • 操作 DOM

對應的 Hook:

useEffect(() => {
  // componentDidMount 的邏輯
}, []) // 空依賴陣列 = 只在掛載時執行

Updating 階段

當 props 或 state 改變時,會依序執行:

shouldComponentUpdate(nextProps, nextState)

shouldComponentUpdate(nextProps, nextState) {
  // 回傳 true 繼續渲染,回傳 false 跳過渲染
  return nextProps.id !== this.props.id;
}

用途:效能優化,決定是否需要重新渲染。

對應的方式: 使用 React.memoReact.PureComponent

render()

重新執行 render() 產生新的 UI。

componentDidUpdate(prevProps, prevState)

componentDidUpdate(prevProps, prevState) {
  // 元件已更新
  if (prevProps.userId !== this.props.userId) {
    this.fetchUser(this.props.userId);
  }
}

用途:

  • 根據 props 變化執行操作
  • 更新後操作 DOM

對應的 Hook:

useEffect(() => {
  // 當 userId 改變時執行
  fetchUser(userId)
}, [userId])

Unmounting 階段

當元件從 DOM 中移除時執行:

componentWillUnmount()

componentWillUnmount() {
  // 清理工作
  this.subscription.unsubscribe();
  clearInterval(this.timerID);
}

用途:

  • 取消訂閱
  • 清除計時器
  • 取消 API 請求

對應的 Hook:

useEffect(() => {
  const subscription = subscribeToData()

  // 清理函式 = componentWillUnmount
  return () => {
    subscription.unsubscribe()
  }
}, [])

Error Handling

componentDidCatch(error, info)

componentDidCatch(error, info) {
  // 捕捉子元件的錯誤
  console.error(error);
  logErrorToService(error, info.componentStack);
}

static getDerivedStateFromError(error)

static getDerivedStateFromError(error) {
  // 更新 state 以顯示錯誤 UI
  return { hasError: true };
}

這兩個方法用於建立 錯誤邊界

生命週期圖解

Mounting:
  constructor → render → componentDidMount

Updating:
  shouldComponentUpdate → render → componentDidUpdate

Unmounting:
  componentWillUnmount

生命週期方法對應 Hooks

Class 生命週期Hook 對應方式
constructoruseState 初始值、useRef
componentDidMountuseEffect(() => {}, [])
componentDidUpdateuseEffect(() => {}, [deps])
componentWillUnmountuseEffect 的清理函式
shouldComponentUpdateReact.memo

完整範例:Class vs Function

Class Component

class Timer extends Component {
  constructor(props) {
    super(props)
    this.state = { seconds: 0 }
  }

  componentDidMount() {
    this.interval = setInterval(() => {
      this.setState((prev) => ({ seconds: prev.seconds + 1 }))
    }, 1000)
  }

  componentWillUnmount() {
    clearInterval(this.interval)
  }

  render() {
    return <p>經過時間:{this.state.seconds} 秒</p>
  }
}

Function Component(相同功能)

function Timer() {
  const [seconds, setSeconds] = useState(0)

  useEffect(() => {
    const interval = setInterval(() => {
      setSeconds((prev) => prev + 1)
    }, 1000)

    return () => clearInterval(interval)
  }, [])

  return <p>經過時間:{seconds} 秒</p>
}

已廢棄的生命週期方法

以下方法已被廢棄,不應再使用:

  • componentWillMount → 使用 constructorcomponentDidMount
  • componentWillReceiveProps → 使用 static getDerivedStateFromProps
  • componentWillUpdate → 使用 getSnapshotBeforeUpdate