React Class Component
Class Component 是 React 早期的元件寫法。現代 React 開發推薦使用 Function Component + Hooks,但了解 Class Component 仍然有幫助,因為你可能會遇到舊的程式碼。
基本語法
import { Component } from 'react'
class Welcome extends Component {
render() {
return <h1>Hello, {this.props.name}</h1>
}
}
Class Component 需要:
- 繼承
Component(或React.Component) - 實作
render()方法
Props
在 Class Component 中,props 透過 this.props 存取:
class Greeting extends Component {
render() {
return (
<div>
<h1>Hello, {this.props.name}</h1>
<p>Age: {this.props.age}</p>
</div>
)
}
}
// 使用
;<Greeting name="Mike" age={25} />
預設 Props
class Button extends Component {
render() {
return <button className={this.props.color}>{this.props.text}</button>
}
}
Button.defaultProps = {
color: 'blue',
text: 'Click me',
}
State
Class Component 使用 this.state 和 this.setState() 來管理狀態:
class Counter extends Component {
constructor(props) {
super(props)
// 初始化 state
this.state = {
count: 0,
}
}
handleClick = () => {
// 更新 state
this.setState({ count: this.state.count + 1 })
}
render() {
return (
<div>
<p>Count: {this.state.count}</p>
<button onClick={this.handleClick}>+1</button>
</div>
)
}
}
setState 是非同步的
// ❌ 可能有問題
this.setState({ count: this.state.count + 1 })
this.setState({ count: this.state.count + 1 }) // 可能還是原來的 count
// ✅ 使用函式形式
this.setState((prevState) => ({ count: prevState.count + 1 }))
this.setState((prevState) => ({ count: prevState.count + 1 }))
setState 的 callback
this.setState({ count: this.state.count + 1 }, () => {
// state 已經更新
console.log('New count:', this.state.count)
})
事件處理與 this 綁定
在 Class Component 中,事件處理函式的 this 需要特別處理:
方法 1:在 constructor 中綁定
class Toggle extends Component {
constructor(props) {
super(props)
this.state = { isOn: false }
// 綁定 this
this.handleClick = this.handleClick.bind(this)
}
handleClick() {
this.setState((prev) => ({ isOn: !prev.isOn }))
}
render() {
return <button onClick={this.handleClick}>{this.state.isOn ? 'ON' : 'OFF'}</button>
}
}
方法 2:使用 Arrow Function(推薦)
class Toggle extends Component {
state = { isOn: false }
// Arrow function 自動綁定 this
handleClick = () => {
this.setState((prev) => ({ isOn: !prev.isOn }))
}
render() {
return <button onClick={this.handleClick}>{this.state.isOn ? 'ON' : 'OFF'}</button>
}
}
Class Fields 語法
使用 Class Fields 可以簡化程式碼,不需要 constructor:
class Counter extends Component {
// 直接定義 state
state = {
count: 0,
}
// Arrow function 作為方法
increment = () => {
this.setState((prev) => ({ count: prev.count + 1 }))
}
decrement = () => {
this.setState((prev) => ({ count: prev.count - 1 }))
}
render() {
return (
<div>
<p>Count: {this.state.count}</p>
<button onClick={this.decrement}>-</button>
<button onClick={this.increment}>+</button>
</div>
)
}
}
Function Component vs Class Component
| 特性 | Function Component | Class Component |
|---|---|---|
| 語法 | 函式 | 類別 |
| 狀態 | useState | this.state + this.setState |
| 生命週期 | useEffect | 生命週期方法 |
| this | 不需要 | 需要處理 this 綁定 |
| 程式碼量 | 較少 | 較多 |
| 效能 | 略好 | 略差 |
| 推薦度 | ✅ 推薦 | ⚠️ 舊版寫法 |
將 Class Component 轉換為 Function Component
轉換前(Class)
class UserProfile extends Component {
state = {
user: null,
loading: true,
}
componentDidMount() {
this.fetchUser()
}
componentDidUpdate(prevProps) {
if (prevProps.userId !== this.props.userId) {
this.fetchUser()
}
}
fetchUser = async () => {
this.setState({ loading: true })
const response = await fetch(`/api/users/${this.props.userId}`)
const user = await response.json()
this.setState({ user, loading: false })
}
render() {
const { user, loading } = this.state
if (loading) return <p>載入中...</p>
return <h1>{user.name}</h1>
}
}
轉換後(Function)
function UserProfile({ userId }) {
const [user, setUser] = useState(null)
const [loading, setLoading] = useState(true)
useEffect(() => {
async function fetchUser() {
setLoading(true)
const response = await fetch(`/api/users/${userId}`)
const data = await response.json()
setUser(data)
setLoading(false)
}
fetchUser()
}, [userId])
if (loading) return <p>載入中...</p>
return <h1>{user.name}</h1>
}
何時還需要 Class Component?
目前,唯一必須使用 Class Component 的情況是 錯誤邊界(Error Boundary),因為它需要使用 componentDidCatch 和 getDerivedStateFromError 這兩個生命週期方法,而這些目前沒有對應的 Hook。
class ErrorBoundary extends Component {
state = { hasError: false }
static getDerivedStateFromError(error) {
return { hasError: true }
}
componentDidCatch(error, info) {
console.error(error, info)
}
render() {
if (this.state.hasError) {
return <h1>發生錯誤</h1>
}
return this.props.children
}
}