參考文章 react源碼, useEffect 完整指南, 嘔心瀝血,一文看懂 react hooks, react官網
在進入正式閱讀以前,最好先思考一下下面的問題:html
我很是喜歡參考文章裏面說的,若是你想要學好React Hooks,那麼你摒棄掉以前組件的想法可能會更加好
爲何我會這麼講?前端
React Hooks會璞歸真,其實就是將咱們以前封裝好的組件對象從新變回來了咱們原始的代碼模式。讓你只要考慮執行過程當中的棧,堆和隊列react
在React Hooks裏面,當咱們聲明一個組件時緩存
import React from 'react'; function App() { return ( <div className="App"> 我是React Hooks </div> ); } export default App;
咱們能夠很明顯的看到,這就是一個函數嘛,只是加入JSX的寫法,返回了組件而已。是的,你沒有理解錯,這應該也是React Hooks的創始人的想法,不比Vue,雖然輕量,可是封裝好了一切,致使可能前端就是學習框架去了~閉包
可是,React不也是組件化的框架嗎?是的,當代碼增多框架
import React, { useState, useEffect } from 'react'; function App() { const [name, setName] = useState('hello'); useEffect(() => { console.log(name) }) return ( <div className="App" onClick={() => {setName(name + 'world')}}> 我是{name} </div> ); } export default App;
讓咱們來大膽的猜測一下,當這段代碼運行完了以後,頁面會發生什麼?less
// 第一次渲染 -----函數開始----- useState定義了一個爲'name'的state useEffect定義了一個函數,他沒有任何的依賴項 返回一個JSX顯示到咱們頁面上 頁面加載成功,發現咱們有個useEffect,發現他不依賴任何屬性,他就要運行,因而他今後次函數中拿到name-->打印hello -----函數結束----- // 當點擊咱們的文本 點擊事件回調修改咱們的state,告訴咱們相應的組件,你須要更新了 -----函數開始----- 從咱們的state中拿出修改後的'name',這時候name爲helloworld 第二次渲染了,useEffect已經被聲明過了,不理他了 返回一個JSX顯示到咱們頁面上 頁面加載成功,發現咱們有個useEffect,發現他不依賴任何屬性,他就要運行,因而他今後次函數中拿到name-->打印helloworld -----函數結束-----
想必,看完了這段例子以後,你對React Hooks也有了必定的理解了。其實就是使用useState去存儲咱們須要存儲的數據,當他更新的時候刷新頁面,當頁面刷新的時候,咱們再使用useEffect來進行咱們須要的操做。ide
這樣看的話,useEffect豈不就是至關於咱們以前的componentDidMount + componentDidUpdate函數
我能夠很負責任的告訴你,不是的,useEffect是咱們更新頁面的反作用,當咱們對他加上了依賴項以後,他就會在頁面加載完了以後,檢查依賴項是否有變化來進行決定是否要運行本身,例如:組件化
import React, { useState, useEffect } from 'react'; function App() { const [name, setName] = useState('hello'); const [myName, setMyName] = useState('my Hello') useEffect(() => { console.log('我是第一個反作用' + name) }) useEffect(() => { console.log('我是第二個反作用' + name) }, []) useEffect(() => { console.log('我是第三個反作用' + name) }, [myName]) useEffect(() => { console.log('我是第四個反作用' + name) }, [name]) return ( <div className="App" onClick={() => {setName(name + 'world')}}> 我是{name} </div> ); } export default App;
這時候咱們執行完了以後和點擊文本以後會發生什麼呢?
// 第一次渲染 -----函數開始----- ...(省略相同步驟) 頁面加載完畢 發現沒有依賴項,打印:我是第一個反作用hello 發現是首次渲染,打印:我是第二個反作用hello 發現是首次渲染,打印:我是第三個反作用hello 發現是首次渲染,打印:我是第四個反作用hello -----函數開始----- // 點擊咱們的文本 更新state -----函數開始----- ...(省略相同步驟) 發現沒有依賴項,打印:我是第一個反作用helloworld 發現依賴項仍是空,與上次相同,不打印 發現myName沒有更新,與上次相同,不打印 發現name更新,打印:我是第四個反作用helloworld -----函數結束-----
詳情可參考例子:https://codesandbox.io/s/reac...
上面的例子可能還沒可以理解爲何是函數式渲染,接下來這一次你可能就能意會到,並且是開發中可能常常出現的問題,看下面的例子
function Counter() { 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> ); }
在該例子中,咱們點擊了Show alert以後,再去點擊Click me,發現彈出來的是了Show alert時候的count,詳情可參考例子:https://codesandbox.io/s/pric...
爲何會出現這樣的狀況呢
由於Counter是一個函數,在點擊Show alert,此時的count爲當前值,在咱們每次點擊Click me的時候Counter函數都會運行一次。這樣是否是就能理解了呢
// 🔴 在條件語句中使用 Hook 違反第一條規則 if (name !== '') { useEffect(function persistForm() { localStorage.setItem('formData', name); }); } // 第二次調用 useState('Mary') // 1. 讀取變量名爲 name 的 state(參數被忽略) // useEffect(persistForm) // 🔴 此 Hook 被忽略! useState('Poppins') // 🔴 2 (以前爲 3)。讀取變量名爲 surname 的 state 失敗 useEffect(updateTitle) // 🔴 3 (以前爲 4)。替換更新標題的 effect 失敗