props
對象並返回一個
React 元素
,它本質上就是基礎的
JavaScript函數。
Class組件的寫法建立方法有兩種,下圖是兼容es5寫法的React.createClass定義的組件,這也是react一開始的建立組件的方法。 前端
propTypes
用來處理校驗定義屬性的類型校驗,PropTypes是從React中獲取的,在React16.0.0版本之後廢棄了這種寫法,PropTypes被單獨提取到prop-types
包中,因此先比較新的版本中,咱們要進行類型校驗,須要手動引入 prop-types 這個模塊getInitialState
用來初始化state,getDefaultProps
用來定義props的默認屬性值,這兩個函數的用法有點相似於如今vue中的data和props方法render函數
,它會返回 react node用來渲染視圖這是一個如今常見的ES6的class react組件寫法 vue
Component API
, 配合一些代碼轉換工具咱們可使用最新的es語法render函數
用來渲染視圖,因爲es6支持了方法簡寫,render以及其餘定義的函數已經不須要聲明function 關鍵字super
調用,ES6 規定,子類的構造函數必須執行一次super方法,若是子類沒有定義constructor方法,這個構造函數會被默認添加並調用super。 若是咱們聲明瞭constructor函數,就必須在構造函數裏調用super,只有在正常調用super(props)方法後才能夠訪問this對象,在constructor中若是要訪問this.props須要在super中傳入props,固然也能夠直接經過props訪問。可是不管有沒有定義constructor,super是否傳入props參數,在react的其餘生命週期中this.props都是可使用的,這是React自動附帶的。字段名 = 引用值
。 在 constructor 方法被執行前, 實例上已經被賦值了該字段內容。
因此如今比較流行的寫法是這樣的 根據公有類字段語法 咱們能夠直接經過
字段名 = state
對象內容來聲明state對象。 因爲箭頭函數沒有本身
this,在這裏它會指向當前類的實例。借用公有類字段的提案,咱們能夠直接聲明一個 箭頭函數來修正方法的this指向。 這樣咱們就無需在構造函數裏去建立state。也不須要在構造函數裏寫一大堆function綁定this。若是沒有其餘的操做,咱們就徹底能夠省略構造函數。
PureComponent
內部是繼承React.Component來的,在React.Component組件中咱們可使用shouldComponentUpdate來判斷是否重發從新渲染,而 React.PureComponent 內部使用 shallowEqual 進行淺比較。 淺比較會比較更新先後的state和props的長度是否一致,判斷是否新增或者減小了props或state的數據個數。 而後它還會調用內部的objectis
方法淺層對比先後的state和prop,objectis相似於es6的 Object.is方法, Object.is 相似於 === 全等運算符,只是在比較 +0 跟 -0 時表現的不太同樣, === 返回的是true 而他是false。node
上圖是一個很簡單的函數組件例子,雖說函數組件本質上就是 一個 JavaScript 函數, 在例子裏看起來沒有任何使用react的api或者方法,可是咱們仍然引入了react,這是由於在組件內部返回的react 元素 使用了 jsx,他實質上是React.createElement
的語法糖,配置好babel就能夠爲咱們編譯jsx,簡化了咱們寫createElement的過程。react
函數組件不須要聲明類,能夠避免大量的譬如extends或者constructor這樣的代碼 也不須要處理 this 指向的問題。 更加純粹的是一個函數就是一個組件,React 官方說的 React 組件一直更像是函數,咱們寫函數式組件彷佛也是更加貼近react的原則。 引用透明性是函數式編程的一個概念,我我的以爲函數組件遵循了純函數的概念。webpack
引用透明性是函數式編程裏的概念。 這是一個 redux 裏的 reducer 函數。在 redux 裏 reducer
須要被定義爲一個純函數,它符合純函數的幾個定義:git
不少時候一開始咱們寫的代碼或者組件都是比較簡單的,咱們可能會選 函數組件來完成一個 簡單的功能模塊。可是越到後面可能功能就變的越發的複雜了,函數組件內可能須要一些本身的狀態或者生命週期了。 這時候想要實現這些功能可能就須要把它藉助 高階組件 或者 render props 幫它包裝一層class 的父組件,這樣它就間接的擁有了狀態跟生命週期。可是這也都只是在函數組件外借助了 class 組件的能力。es6
因爲函數組件在每次渲染時候,組件內部都會從上到下從新執行一遍,它也沒有辦法有本身內部的狀態,也沒法產生反作用,爲了加強函數組件,hook
就出現了。github
Vue 在放出可能到來 3.0 更新的內容,上圖就是此次更新變更比較大的 funtion based api
.web
對比右側2.0的寫法, template 就是 以前的模板語法。比較特殊的就是 setup 函數。 這裏的value是一個包裝對象,它就是組件內部的狀態,這個看起來跟react 的 usestate hook 仍是很是像的。 onMounted 對應的就是之前的生命週期函數 mounted,方法最後的返回對象有點像以前的data方法裏的內容被綁定到了template模板語法上,setup 看起來就是一個簡單的函數。typescript
vue做者提到了function based api
借鑑了
react hook
的思想。 因爲
typescript對函數的類型推導能力比較好,以前vue組件比較流行的那種對象的寫法如今看起來也變成了setup這樣一個函數了。 至於更靈活的邏輯複用能力,這個在後面會看到是如何進行邏輯複用的。
React 16.8
的新增特性。它是在函數組件的基礎上引入了一些新API,可讓你在不編寫
class 的狀況下使用
state 以及其餘的 React 特性。
咱們看到這裏使用了一個 useState
的API,它能夠在調用時傳入參數,而且返回一個數組。數組中有兩項內容,第一個是狀態(state)、第二個是設置狀態(setState),使用es6 數組解構的特性,咱們能夠根據咱們的須要去語義化命名,useState中的init參數只會在方法在第一次初始化之後,即便函數組件被更新,它的state也不會被重置,因此它能夠一直保留着當前的狀態。 這也就讓函數組件擁有了內部狀態,以及生命週期。
在react 的 class 組件中,咱們會定義一個數組類型的 state,在對修改了引用中data裏的一些值後,調用this.setState 方法會觸發從新渲染,可是在一樣的操做在hook裏並不會觸發更新。
調用咱們設置 hook 的 setData 更新函數,數並傳入當修改過引用類型的data 去觸發更新時,React將跳過子組件的渲染及 effect 的執行。(React 使用 Object.is 比較先後 state)因爲引用地址沒變化 因此不會發生從新渲染。 咱們可使用圖中下面的兩種方式淺拷貝對象 而後再進行操做,便可解決這一問題。useEffect的規則以下:
React內部會建立對應的 hook 節點,react 第一次初始化組件時內部會將這些節點結構經過 next 依次鏈接起來,造成一個單向鏈表結構,每一次調用setter 就 dispach 一個對應的action方法,將action存儲在queue中。
在後續觸發渲染時,react 會調用 queue 隊列中的內容,queue 也是一個鏈表結構,它是收集咱們調用setter方法,queue 會依次執行內部的action,從而獲取到最新的狀態值,狀態值會被更新到memorizedState上,而後將狀態反應到對應的hook字段中造成綁定。因此當咱們打亂或者增長減小hooks時,會形成hook內部順序錯誤,從而致使沒法正常工做。
上圖是組合成的一個hooks單向鏈表結構,內部依次經過next指向下一個hook。
嘗試經過修改hook數量,在第一次初始化時候咱們調用了三個usestate hook,react也會在它內部依次收集了三個hook。 第一次渲染時,頁面能夠正常的渲染出內容,當咱們點擊了一個 setter 方法,從新觸發了渲染時,會因爲 hook 數量比以前的少而致使頁面錯誤,沒法正常渲染。因此保證每次都是相同的調用順序才能正確的使用hook。
Hooks API是專門爲函數組件設計的,因此咱們只能在函數式組件內部或者自定義的hook中使用hooks,包括usestate、useeffect、userref 等以及自定義的hook api。並且自定義hook,也是沒法在class組件內部使用的。 固然正常的函數組件,即便在內部使用了hook,咱們能夠也class組件內部複用該函數組件,這個是不會影響的。componentDidMount: 當第二個參數爲空數組時,回調只會被觸發一次,相似於componentDidMount。
componentDidUpdate: 當 useEffect 不傳入第二個參數時,在第一次初始化和每次從新渲染後都會觸發回調。這與 componentDidUpdate 有些不一樣,componentDidUpdate 生命週期在初始化掛載的時候不會被調用,咱們可使用 useRef 鉤子來存儲一個值來判斷函數是否爲第一次調用useRef返回一個可變的ref對象,它不只能夠綁定dom的引用,也能夠用來存儲一些值。其.current
屬性被初始化爲傳遞的參數(initialValue)。返回的對象將持續整個組件的生命週期。變動 .current
屬性不會引起組件從新渲染。
componentWillUnmount 前面提到每一個 useEffect 均可以返回一個清除函數。配合這一特性,咱們能夠任然將第二個參數設置爲一個空數組,這樣清除函數會在組件卸載前被調用,咱們能夠在這裏面清除一些事件監聽器等。更方便的是咱們能夠將 didMount 和 unMount 寫在同一個 useEffect 鉤子中。
componentWillReceiveProp: 咱們想要模擬以前的 componentWillReceiveProps 生命函數,能夠將第二個參數設爲須要觀察的props參數。爲了比對先後的 props 變化,能夠用 useRef 來存儲舊的props來達到目的。setState的第二個參數 咱們知道在使用setState方法時,能夠傳入第二個參數,這個參數是個回調函數,它在設置完 state 之後 會被調用。 咱們在hook裏也能夠經過用useEffect 鉤子傳入要監聽的 state,當該state更新之後,回調函數會被調用。
React 規定 自定義hook 須要用 use 開頭來定義,自定義hook雖然不是一個組件,它不須要返回 React Node 元素節點,若是返回了元素,它就又變成的一個函數組件,可是雖然這樣,它一樣的能夠在方法內部使用 狀態 usestate 和生命週期 useeffect 等其餘的 hooks。他擁有本身的內部狀態和生命週期。
比較特殊的是 自定義hook 返回的內容能夠任何類型,你能夠單純的返回一個值,也能夠返回一個對象或者數組甚至是方法,返回的內容取決於在調用你建立的自定義hook時所須要的一些屬性。
咱們在這裏定義了一個名爲 useOnline 的自定義hook, 咱們用它來判斷當前的網絡鏈接狀態。首先hook的方法名是以use開頭的,後面的名字能夠根據你的意願來定義,大部分時候hook都會遵循駝峯命名法,只要是以use開頭,後面的定義是沒有限制的。不過我嘗試了一下即便不使用use開頭建立的自定義hook其實也是正常運行的。react 約定以use開頭是爲了經過一些自動檢查工具來來校驗 這些use開頭的hook內部是否違反了 hook的使用規則。不過仍是最好遵循使用use開頭,駝峯命名這樣一個約定。
而後調用 usestate 建立一個 自定義hook內部的狀態, 傳入了 navigator online 初始化當前的網絡狀態,而且在自定義 hook 內部擁有了本身的狀態。
接着在使用 useeffect 鉤子內,咱們須要監聽網絡狀態的變化,聯網或者斷網的監聽方法只須要在組件建立時調用一次便可,因此咱們在傳遞useeffect的第二個參數時,只傳遞了一個空的數組,這樣它就只會在 didmount 時被調用一次。
一樣的在銷燬組件時,咱們也須要清除掉這些監聽事情。在 effect 鉤子的返回函數中清理掉這兩個監聽方法便可
最後咱們將 當前的狀態值 做爲自定義hook的返回值,這樣就完成了一個監聽網絡狀態的自定義hook。
咱們在一個最簡單的函數組件中引入這個hook。 而後在函數組件內咱們調用這個自定義hook,hook會返回當前的網絡狀態,這是一個很簡單的函數組件,咱們只渲染當前的網絡狀態。
當咱們渲染出組件時候他渲染出了當前的網絡狀態, 當咱們切換爲離線狀態時,能夠看組件被從新渲染出了當前的網絡狀態值。這樣,當咱們建立出一個自定義hook時 就能夠在不少地方調用直接複用這段代碼,達到了邏輯複用的目的。
不過你們可能以爲這不就是抽象一個方法麼? 好像沒有自定義hook 咱們也能夠把一些方法抽象到一些工具函數中, 寫一些 helper 或者 utils來完成這些功能 。 可是實際上是不同的,自定義的hook強大之處在於擁有狀態,當狀態值改變時,它能夠觸發調用組件的從新渲染,並且自定義hook能夠跟隨着組件的生命週期,在不一樣的生命鉤子階段,咱們能夠處理一些事件。
咱們能夠選擇本身去 實現 一個 useDidMount 自定義hook,實現起來也很是簡單。只須要在鉤子內部調用 useeffect hook,將它的第一個參數傳遞爲咱們自定義鉤子的回調函數參數,將第二個可選的參數設爲一個空數組,這樣就只會在 didmount時候被調用一次。
這個開源項目幫咱們收集了一系列已經封裝好的自定義hook,好比這些生命週期鉤子,咱們只須要引入便可實現這些生命週期。包括一些 didMount, unmount, update 等鉤子。
分享一個React Hook弧形進度條組件react-arc-progress,這個以前是一個es6方式寫的插件,而後以前爲了跟進 react hook,將它改形成了一個 react 組件。
重複的造輪子確定是沒有意義的,因此要添加一些獨特的功能讓它變得更不同些:
GitHub地址,請給個star吧 ◔ ‸◔