自 React 16.8 發佈之後,在已有項目中,把 package.json 中的 react 和 react-dom 版本一升,就能夠抄起 Hooks 開幹了。筆者目前已經在項目中開始了實操,但不妨先總結下官方文檔中一些值得梳理的點。javascript
也就是說,create 的叫法就不太符合初始渲染以後獲取到的是「當前狀態」這麼一個事實了。html
每一個組件內部都有一個「內存格子」的列表,他們就是一些存放數據的 JS 對象,當咱們使用如 useState 的 Hooks 時,就會去讀取當前的格子(或者在初始渲染的時候進行初始化),而後將指針移動到下一個 Hooks。這就是爲何一個組件內部的多個 useState 都能獲取到各自的局部狀態。java
可是須要注意的是,這也是爲何官方建議咱們要將 hooks 的調用順序保持一致。react
其一,React 會在每次渲染完成後會調用 useEffect,若是使用傳統的生命週期鉤子的話,當咱們但願每次 render 後執行某種反作用時,咱們不得不在 componentDidMount 和 componentDidUpdate 裏都塞上相同的邏輯,帶來冗餘。所以,傳統的生命週期是不能代替 useEffect 的。這一點可參考 React Class 生命週期。json
固然,相比較考慮 mount 和 update,只考慮 render 是要簡單清晰很多。bash
其二,Hooks 讓咱們能夠基於邏輯而拆分代碼,而不是基於生命週期。這一點很是重要,由於基於生命週期來拆分代碼,勢必讓邏輯相關聯的代碼分散各處。使用 Hooks,咱們就能夠按照咱們指定的順序使用每個反作用。dom
是的,這是爲了保證在 useEffect 中使用到的內部狀態都是最新的。這樣 useEffect 就很像是 render 的一部分了 —— 每次使用的 useEffect 都屬於其對應的的 render。ide
不只如此,咱們在 useEffect 中 return 的方法,也即一般用來作取消訂閱這類 cleanup 工做的,每次 render 後也都會執行一次新的反作用(準確的說會先走 return 的方法,再從新走一次 useEffect 中的方法),而毫不是 unmount 的時候才執行一次。這種模式會有更少的 bug。函數
什麼樣的 bug 呢?能夠看官方文檔的例子,大體就是說,若是咱們訂閱的人的 id 變了,就須要取消訂閱而後從新訂閱新的人。這樣一來,若是在使用 class 作訂閱這類處理時,就須要在 3 個生命週期(componentDidMount、componentDidUpdate、componentWillUnmount)裏散佈邏輯,即在 componentDidUpdate 補充上取消並從新訂閱的邏輯!oop
若是用了 useEffect,這些東西根本不須要去考慮。整個過程如文檔中給的例子同樣依次執行:
function FriendStatus(props) {
// ...
useEffect(() => {
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
});
// ...
}
// Mount with { friend: { id: 100 } } props
ChatAPI.subscribeToFriendStatus(100, handleStatusChange); // Run first effect
// Update with { friend: { id: 200 } } props
ChatAPI.unsubscribeFromFriendStatus(100, handleStatusChange); // Clean up previous effect
ChatAPI.subscribeToFriendStatus(200, handleStatusChange); // Run next effect
// Update with { friend: { id: 300 } } props
ChatAPI.unsubscribeFromFriendStatus(200, handleStatusChange); // Clean up previous effect
ChatAPI.subscribeToFriendStatus(300, handleStatusChange); // Run next effect
// Unmount
ChatAPI.unsubscribeFromFriendStatus(300, handleStatusChange); // Clean up last effect
複製代碼
對 return 的 cleanup 一樣適用,不要忘了,每次 render 完就會先執行一次 cleanup,最終 unmount 的時候也會執行一次 cleanup。
useEffect(() => {
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
}, [props.friend.id]); // 只會在 props.friend.id 變化的時候從新訂閱
複製代碼
若是咱們不提供該參數,每次更新都會從新執行;若是隻想 mount 和 unmount 的時候各執行一次,可指定 [],但這不是好的實踐方式,考慮到 useEffect 都是在 render 完後執行的,多作點工做可能會少點問題。
Only Call Hooks at the Top Level. Don’t call Hooks inside loops, conditions, or nested functions.
這一條的緣由是,Hooks 是經過調用順序分配存放位置的,只有每次 run 的時候順序保持一致,才能挨個取得正確的 useState、useEffect。比方說,若是咱們把 Hooks 放到條件語句裏,而後第一次 render 的時候每一個都執行,第二次 render 卻有一個 Hook 不執行,那麼後面的對應就出錯了。很好理解吧。
但若是咱們必定要有條件的執行 useEffect 呢?咱們能夠在 useEffect 內部加條件:
useEffect(function persistForm() {
// 👍 這樣就不會破壞第一條原則
if (name !== '') {
localStorage.setItem('formData', name);
}
});
複製代碼
Only Call Hooks from React Functions.
這條沒什麼說的,總之只在下面兩處用 Hooks: