今早來又莫名其妙的遇到了 bug,排查了一下是 useEffect 導致的。今天就再來詳細的學習一下 react useEffect。
為什么要?
我們知道,react 的函數組件里面沒有生命周期的,也沒有 state,沒有 state 可以用 useState 來替代,那么生命周期呢?
useEffect 是 react v16.8 新引入的特性。我們可以把 useEffect hook 看作是componentDidMount、componentDidUpdate、componentWillUnmounrt三個函數的組合。
詳解
原來我們是這么寫 class component
class LifeCycle extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
}
componentDidMount() {
document.title = `you clicked me ${this.state.count} times`;
}
componentDidUpdate() {
document.title = `you clicked me ${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 來寫 function component
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>
)
}
上面的這段代碼,每次 count 變更以后,effect都會重新執行。
我們日常開發中,獲取數據、訂閱以及手工更改 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>
}
我們可以讓 React 跳過對 effect 的調用,讓 effect 的執行依賴於某個參數(傳利數組作為 useEffect 的第二個可選參數就可以了)。倘若僅僅只想執行一次,那么就傳遞一個空的數組作為第二個參數,這個時候 useEffect hook 不依賴於 props 或者任何值,所以永遠都不重復執行。
在過去的性能優化里,在某些情況下,每次渲染后都執行清理或者執行 effect 都可能會導致性能的問題。在 class 組件中,我們通過在 componentDidUpdate 添加 prevProps或者 prevState 來解決。
componentDidUpdate(prevProps, prevState) {
if(prevState.count !== this.state.count) {
document.title = `You clicked ${this.state.count} times`
}
}
這個是非常常見的需求。而如今,倘若我們使用 function component,代碼會非常的簡潔。
useEffect(() => {
document.title = `You clicked ${count} times`;
}, [count])
總結
Hook 是對函數式組件的一次增強,v16.8 出現以后才出來的 hook 特性。在這之前,我看有的代碼里面,通過一個<InitialCallBack>來放到函數時組件的最前面,通過在<InitialCallBack>的 componentDidMount 來獲取后端接口數據。
就是變相的往 Hook 里面引入生命周期。
然而 v16.8 出來了,這個問題完全解決了。
Hook的語法更簡潔易懂,消除了 class 的聲明周期方法導致的重復邏輯代碼。
同時,在某種程度上解決了高階組件難以理解和使用的問題。
然而 Hook 並沒有讓函數時組件做到 class 組件做不到的事情,只是讓很多組件變得寫的更簡單。class 組件不會消失,hook 化的函數時組件將是趨勢。