React Hooks 原理


類被會替代嗎?

Hooks不會替換類,它們只是一個你可以使用的新工具。React 團隊表示他們沒有計划在React中棄用類,所以如果你想繼續使用它們,可以繼續用。

我能體會那種總有新東西要學的感覺有多痛苦,不會就感覺咱們總是落后一樣。Hooks 可以當作一個很好的新特性來使用。當然沒有必要用 Hook 來重構原來的代碼, React團隊也建議不要這樣做。

 

Go Go

來看看Hooks的例子,咱們先從最熟悉的開始:函數組件。

以下 OneTimeButton 是函數組件,所做的事情就是當我們點擊的時候調用 sayHi 方法。

import React from 'react'; import { render } from 'react-dom'; function OneTimeButton(props) { return ( <button onClick={props.onClick}> 點我點我 </button> ) } function sayHi() { console.log('yo') } render( <OneTimeButton onClick={sayHi}/>, document.querySelector('#root') ) 

我們想讓這個組件做的是,跟蹤它是否被點擊,如果被點擊了,禁用按鈕,就像一次性開關一樣。

但它需要一個state,因為是一個函數,它不可能有狀態(React 16.8之前),所以需要重構成類。

函數組件轉換為類組件的過程中大概有5個階段:

*否認:也許它不需要是一個類,我們可以把 state 放到其它地方。

  • 實現: 廢話,必須把它變成一個class,不是嗎?
  • 接受:好吧,我會改的。
  • 努力加班重寫:首先 寫 class Thing extends React.Component,然后 實現 render等等 。
  • 最后:添加state。
class OneTimeButton extends React.Component { state = { clicked: false } handleClick = () => { this.props.onClick(); // Ok, no more clicking. this.setState({ clicked: true }); } render() { return ( <button onClick={this.handleClick} disabled={this.state.clicked} > You Can Only Click Me Once </button> ); } } 

這是相當多的代碼,組件的結構也發生了很大的變化, 我們需要多個小的功能,就需要改寫很多。

 

使用 Hook 輕松添加 State

接下來,使用新的 useState hook向普通函數組件添加狀態:

import React, { useState } from 'react' function OneTimeButton(props) { const [clicked, setClicked] = useState(false) function doClick() { props.onClick(); setClicked(true) } return ( <button onClick={clicked ? undefined : doClick} disabled={clicked} > 點我點我 </button> ) }

 

這段代碼是如何工作的

這段代碼的大部分看起來像我們一分鍾前寫的普通函數組件,除了useState。

useState是一個hook。 它的名字以“use”開頭(這是Hooks的規則之一 - 它們的名字必須以“use”開頭)。

useState hook 的參數是 state 的初始值,返回一個包含兩個元素的數組:當前state和一個用於更改state 的函數。

類組件有一個大的state對象,一個函數this.setState一次改變整個state對象。

函數組件根本沒有狀態,但useState hook允許我們在需要時添加很小的狀態塊。 因此,如果只需要一個布爾值,我們就可以創建一些狀態來保存它。

由於Hook以某種特殊方式創建這些狀態,並且在函數組件內也沒有像setState函數來更改狀態,因此 Hook 需要一個函數來更新每個狀態。 所以 useState 返回是一對對應關系:一個值,一個更新該值函數。 當然,值可以是任何東西 - 任何js類型 - 數字,布爾值,對象,數組等。

現在,你應該有很多疑問,如:

  • 當組件重新渲染時,每次都不會重新創建新的狀態嗎? React如何知道舊狀態是什么?
  • 為什么hook 名稱必須以“use”開頭? 這看起來很可疑。
  • 如果這是一個命名規則,那是否意味着我可以自定義 Hook。
  • 如何存儲更復雜的狀態,很多場景不單單只有一個狀態值這么簡單。

 

Hooks 的魔力

將有狀態信息存儲在看似無狀態的函數組件中,這是一個奇怪的悖論。這是第一個關於鈎子的問題,咱們必須弄清楚它們是如何工作的。

原作者得的第一個猜測是某種編譯器的在背后操眾。搜索代碼useWhatever並以某種方式用有狀態邏輯替換它。

然后再聽說了調用順序規則(它們每次必須以相同的順序調用),這讓我更加困惑。這就是它的工作原理。

React第一次渲染函數組件時,它同時會創建一個對象與之共存,該對象是該組件實例的定制對象,而不是全局對象。只要組件存在於DOM中,這個組件的對象就會一直存在。

使用該對象,React可以跟蹤屬於組件的各種元數據位。

請記住,React組件甚至函數組件都從未進行過自渲染。它們不直接返回html。組件依賴於React在適當的時候調用它們,它們返回的對象結構React可以轉換為DOM節點。

React有能力在調用每個組件之前做一些設置,這就是它設置這個狀態的時候。

其中做的一件事設置 Hooks 數組。 它開始是空的, 每次調用一個hook時,React 都會向該數組添加該 hook。

 

為什么順序很重要

假設咱們有以下這個組件:

function AudioPlayer() { const [volume, setVolume] = useState(80); const [position, setPosition] = useState(0); const [isPlaying, setPlaying] = useState(false); ..... } 

因為它調用useState 3次,React 會在第一次渲染時將這三個 hook 放入 Hooks 數組中。

下次渲染時,同樣的3個hooks以相同的順序被調用,所以React可以查看它的數組,並發現已經在位置0有一個useStatehook ,所以React不會創建一個新狀態,而是返回現有狀態。

這就是React能夠在多個函數調用中創建和維護狀態的方式,即使變量本身每次都超出作用域。

 

多個useState 調用示例

讓咱們更詳細地看看這是如何實現的,第一次渲染:

  1. React 創建組件時,它還沒有調用函數。React 創建元數據對象和Hooks的空數組。假設這個對象有一個名為nextHook的屬性,它被放到索引為0的位置上,運行的第一個hook將占用位置0。
  2. React 調用你的組件(這意味着它知道存儲hooks的元數據對象)。
  3. 調用useState,React創建一個新的狀態,將它放在hooks數組的第0位,並返回[volume,setVolume]對,並將volume 設置為其初始值80,它還將nextHook索引遞增1。
  4. 再次調用useState,React查看數組的第1位,看到它是空的,並創建一個新的狀態。 然后它將nextHook索引遞增為2,並返回[position,setPosition]。
  5. 第三次調用useState。 React看到位置2為空,同樣創建新狀態,將nextHook遞增到3,並返回[isPlaying,setPlaying]。

現在,hooks 數組中有3個hook,渲染完成。 下一次渲染會發生什么?

  1. React需要重新渲染組件, 由於 React 之前已經看過這個組件,它已經有了元數據關聯。
  2. React將nextHook索引重置為0,並調用組件。
  3. 調用useState,React查看索引0處的hooks數組,並發現它已經在該槽中有一個hook。,所以無需重新創建一個,它將nextHook推進到索引1並返回[volume,setVolume],其中volume仍設置為80。
  4. 再次調用useState。 這次,nextHook為1,所以React檢查數組的索引1。同樣,hook 已經存在,所以它遞增nextHook並返回[position,setPosition]。
  5. 第三次調用useState,我想你知道現在發生了什么。

就是這樣了,知道了原理,看起來也就不那么神奇了, 但它確實依賴於一些規則,所以才有使用 Hooks 規則。

資源搜索網站大全 https://www.renrenfan.com.cn 廣州VI設計公司https://www.houdianzi.com

Hooks 的規則

自定義 hooks 函數只需要遵守規則 3 :它們的名稱必須以“use”為前綴。

例如,我們可以從AudioPlayer組件中將3個狀態提取到自己的自定義鈎子中:

function AudioPlayer() { // Extract these 3 pieces of state: const [volume, setVolume] = useState(80); const [position, setPosition] = useState(0); const [isPlaying, setPlaying] = useState(false); // < beautiful audio player goes here > } 

因此,咱們可以創建一個專門處理這些狀態的新函數,並使用一些額外的方法返回一個對象,以便更容易啟動和停止播放,例如:

function usePlayerState(lengthOfClip) { const [volume, setVolume] = useState(80); const [position, setPosition] = useState(0); const [isPlaying, setPlaying] = useState(false); const stop = () => { setPlaying(false); setPosition(0); } const start = () => { setPlaying(true); } return { volume, position, isPlaying, setVolume, setPosition, start, stop }; } 

像這樣提取狀態的一個好處是可以將相關的邏輯和行為組合在一起。可以提取一組狀態和相關事件處理程序以及其他更新邏輯,這不僅可以清理組件代碼,還可以使這些邏輯和行為可重用。

另外,通過在自定義hooks中調用自定義hooks,可以將hooks組合在一起。hooks只是函數,當然,函數可以調用其他函數。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM