上一篇介紹過 State Hook 用來儲存狀態,Effect Hook 則用來處理 function component 中的副作用,像是資料 fetch、或手動改變 DOM 等等。使用 Class 跟 Effect Hook 差別能感受到不同切割 React 更新的邏輯,從固定的時間到更大彈性的 render 前後變化。 Effect Hook 對照 Class 生命週期也可以看作是 componentDidMount,componentDidUpdate 和 componentWillUnmount 的組合,夠大的意義是將這些分散的不同地方的邏輯,但其實是 建構與清除(ex: addEventListener, removeEventListener, setTimeout, clearTimeout) 成對的邏輯,重新集中關注點。
function TestUseState() {
const [title, setTitle] = React.useState("I am waiting...");
const [count, setCount] = React.useState(0);
React.useEffect(() => {
async function getTitle() {
let response = await fetch('https://jsonplaceholder.typicode.com/todos/1')
response = await response.json()
setTitle(response.title)
}
getTitle()
});
React.useEffect(() => {
// 使用瀏覽器 API 更新文件標題
document.title = `You clicked ${count} times`;
});
return (
<div>
<h1>Hello UseEffect Hook</h1>
<h3>Title Async : { title }</h3>
<h3>Title Count : { count } times</h3>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
)
}
當 side effect 使用到 setTimeout 、監聽事件或其他外部資源時,就需要注意去清除 timer、listener 或外部資源 socket 等。而清除使用 return function 的方式,
useEffect(() => {
const timer = setTimeout(() => {
console.log('This will run after 1 second!')
}, 1000);
return () => clearTimeout(timer);
}, []);
告訴 React 你的 component 需要在 render 後做一些事情。通常 componentDidMount 和 componentWillUnmount 成對的,但生命週期中只會執行一次,而中間如有變化可能會造成錯誤。也就是為什麼 在 component 內部呼叫 useEffect,比起用時間週期來切分, render 前後切分降低在撰寫 建造與清除 的程式可以更集中。
=> 只執行一次 unmounting 可能發生的錯誤,當中間又觸發 prop ,unsubscribe 的部分可能 cancel 掉錯誤的 id 造成 memory leak。
Class Component 的 Bug 例子:
componentDidMount() {
ChatAPI.subscribeToFriendStatus(
this.props.friend.id,
this.handleStatusChange
);
}
componentWillUnmount() {
ChatAPI.unsubscribeFromFriendStatus(
this.props.friend.id,
this.handleStatusChange
);
}
=> 使用第二個參數,可以夠有效率的監控資料變化
useEffect(() => {
document.title = `You clicked ${count} times`;
}, [count]);
以上今天。
參考資料:
https://zh-hant.reactjs.org/docs/hooks-effect.html#tip-optimizing-performance-by-skipping-effects
https://stackoverflow.com/questions/61885496/how-to-cancel-socket-io-client-subscription-in-react