前言
在使用Hooks時常常會出現一些有趣的問題,這些問題往往跟你之前的Component組件的思維有關。
Hooks與Component
Hooks的useEffect的確可以模擬部分class組件中生命周期的方法,但并不能把兩者等同。因為兩者本身是不同的(看起來像是一句廢話)
Function組件是沒有實例的,而Component組件是有實例的,這會導致在Function組件中沒有辦法引用和保證前一個state,而Component組件是可以的,所以Hooks添加了useRef。
可以再看下兩者在渲染時的差異
Function 組件
function Example() {
const [count, setCount] = useState(0);
function handleAlertClick() {
setTimeout(() => {
alert('You clicked on: ' + count);
}, 3000);
}
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
<button onClick={handleAlertClick}>
Show alert
</button>
</div>
);
}
Component組件
class Example extends React.Component{
state={
count:0
}
handleAlertClick=()=> {
setTimeout(() => {
alert('You clicked on: ' + this.state.count);
}, 3000);
}
render(){
const {count}=this.state;
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => this.setState({count:count+1})}>
Click me
</button>
<button onClick={this.handleAlertClick}>
Show alert
</button>
</div>
);
}
}
同樣的操作,比如點擊Click Me按鈕,先點擊3次,然后點擊Show alert按鈕,再點擊Click Me按鈕2次。
那么同樣的操作在兩者中的alert結果是不同的:
- Function組件:
alert的是3 - Component組件:
alert的是5
原因在于Function組件的render相當于對當前狀態進行了一次快照,props、state和其他事件函數等等都是重新生成的且獨立的。這不同于Component組件,Component組件的props、state和事件函數可以保存在實例上,不需要重新生成,也就是實時的。
當然上面的Function組件例子中也有涉及到閉包相關的東西。
Hooks與閉包
Function組件在使用Hooks時會產生閉包。
在查看Hooks源碼后也能體現出來。
閉包的其中一個特性是可以保持一個變量的持續引用。
再加上Function組件每次render都是一次獨立的快照,即重新生成的了一個全新的函數。
那么當之前的異步函數發生調用時,由于閉包的特性,還保持了對于之前的引用,所以就導致了上述例子Function組件alert出來的是3。
由于Component組件的思維模式,在Hooks的開發中,常常會導致一些問題,其他很大一部分就是由于Function的快照與閉包導致的。
參考:
1.Hook 概覽——React官方文檔
2.Hooks FAQ——React官方文檔
3.簡單聊一聊 hooks 與閉包——Limboer
4.a-complete-guide-to-useeffect——Dan Abramov