之前我們已經掌握了useState的使用,在 class 中,我們通過在構造函數中設置 this.state
為 { count: 0 }
來初始化 count
state 為 0
:
class Example extends React.Component { constructor(props) { super(props); this.state = { count: 0 }; }
在函數組件中,我們沒有 this
,所以我們不能分配或讀取 this.state
。我們直接在組件中調用 useState
Hook:
import React, { useState } from 'react'; function Example() { // 聲明一個叫 “count” 的 state 變量 const [count, setCount] = useState(0);
既然我們知道了 useState
的作用,那么掌握 useEffect
就更容易,函數組件中沒有生命周期,那么可以使用 useEffect
來替代。如果你熟悉 React class 的生命周期函數,你可以把 useEffect
Hook 看做 componentDidMount
,componentDidUpdate
和 componentWillUnmount
這三個函數的組合。
class Example extends React.Component { constructor(props) { super(props); this.state = { count: 0 }; } componentDidMount() { document.title = `You clicked ${this.state.count} times`; } componentDidUpdate() { document.title = `You clicked ${this.state.count} times`; } render() { return ( <div>; <p>You clicked {this.state.count} times</p>; <button onClick={() =>; this.setState({ count: this.state.count + 1 })}> Click me </button>; </div>; ); } } 使用 Hook 的示例 import React, { useState, useEffect } from 'react'; function Example() { const [count, setCount] = useState(0); useEffect(() => { document.title = `You clicked ${count} times`; }); return (
<div><p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); }
使用 Hook 的示例
import React, { useState, useEffect } from 'react'; function Example() { const [count, setCount] = useState(0); useEffect(() => { document.title = `You clicked ${count} times`; }); return ( <div><p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); }
默認情況下,它在第一次渲染之后和每次更新之后都會執行。你可能會更容易接受 effect 發生在“渲染之后”這種概念,不用再去考慮“掛載”還是“更新”。React 保證了每次運行 effect 的同時,DOM 都已經更新完畢。
數據獲取,設置訂閱以及手動更改 React 組件中的 DOM 都屬於副作用。有些副作用可能需要清除,所以需要返回一個函數,比如掛載時設置定時器,卸載時取消定時器。
class Example extends Component { constructor (props) { super(props); this.state = { count: 0 } } componentDidMount() { this.id = setInterval(() => { this.setState({count: this.state.count + 1}) }, 1000); } componentWillUnmount() { clearInterval(this.id) } render() { return <h1>{this.state.count}</h1>; } }
使用 Hook 的示例
function Example() { const [count, setCount] = useState(0); useEffect(() => { const id = setInterval(() => { setCount(c => c + 1); }, 1000); return () => clearInterval(id); }, []); return <h1>{count}</h1> }
useEffect(effect: React.EffectCallback, inputs?: ReadonlyArray<any> | undefined)
你可以通知 React 跳過對 effect 的調用,只要傳遞數組作為 useEffect
的第二個可選參數即可,如果想執行只運行一次的 effect(僅在組件掛載和卸載時執行),可以傳遞一個空數組([]
)作為第二個參數。這就告訴 React 你的 effect 不依賴於 props 或 state 中的任何值,所以它永遠都不需要重復執行。
通過跳過 Effect 進行性能優化,在某些情況下,每次渲染后都執行清理或者執行 effect 可能會導致性能問題。在 class 組件中,我們可以通過在 componentDidUpdate
中添加對 prevProps
或 prevState
的比較邏輯解決:
componentDidUpdate(prevProps, prevState) { if (prevState.count !== this.state.count) { document.title = `You clicked ${this.state.count} times`; } }
這是很常見的需求,所以它被內置到了 useEffect
的 Hook API 中。如果某些特定值在兩次重渲染之間沒有發生變化,你可以通知 React 跳過對 effect 的調用,只要傳遞數組作為 useEffect
的第二個可選參數即可:
useEffect(() => { document.title = `You clicked ${count} times`; }, [count]); // 僅在 count 更改時更新
你已經學習了 State Hook 和 Effect Hook,將它們結合起來你可以做很多事情了。它們涵蓋了大多數使用 class 的用例。