首頁(yè)技術(shù)文章正文

hooks函數(shù)是什么?怎么用?

更新時(shí)間:2020-07-27 來(lái)源:黑馬程序員 瀏覽量:

"Hooks到底是個(gè)啥玩意兒???"

你是不是有這樣的疑惑?在你自認(rèn)為已經(jīng)了解了差不多 React 所有的內(nèi)容的時(shí)候,Hooks 就這么出現(xiàn)了。

這就是前端開(kāi)發(fā)人員的日常,技術(shù)從未停止更新。

學(xué)習(xí)新東西是一件很棒的事情嗎?當(dāng)然是!但有的時(shí)候我們又不得不思考:”為什么要學(xué)它?這個(gè)新東西有啥意義?我是必須得學(xué)他嗎?“。

針對(duì)Hooks而言,上面的這個(gè)問(wèn)題答案是否定的,你不需要立馬就學(xué)它。如果您一直在使用 React,并且迄今為止一直在使用基于類的組件,那么就不必急于轉(zhuǎn)向 Hooks。Hooks 是可選的,可以與現(xiàn)有組件配合使用。我相信當(dāng)你因?yàn)橐褂眯聳|西而不得不重寫(xiě)整個(gè)代碼庫(kù),你整個(gè)人都是崩潰的。

在函數(shù)組件中使用狀態(tài)

在使用 Hooks 之前,我們不能在函數(shù)組件中使用狀態(tài)。這就意味著,如果您有一個(gè)經(jīng)過(guò)精心設(shè)計(jì)和測(cè)試的函數(shù)組件,突然需要存儲(chǔ)狀態(tài),那么你就不得不把他重構(gòu)為一個(gè)類組件。

牛逼的來(lái)了,Hooks 讓函數(shù)組件也能使用狀態(tài),就意味著我們不需要重構(gòu)之前自己的寫(xiě)的代碼,[可以點(diǎn)擊這篇文章查看更多]( https://scotch.io/courses/5-essential-react-concepts-to-know-before-learning-redux/presentational-and-container-component-pattern-in-react)。

類組件很笨重

我們不的不承認(rèn)的是,類組件附帶了太多的東西。constructor,binding,“this”無(wú)處不在。使用函數(shù)組件可以消除許多這種情況,能讓我們的代碼更容易維護(hù)。

可以在[React文檔中了解更多相關(guān)內(nèi)容](https://reactjs.org/docs/hooks-intro.html#classes-confuse-both-people-and-machines)


更高的可讀性

由于 Hooks 允許我們?cè)诤瘮?shù)組件中使用狀態(tài),因此和類組件相比,這意味同樣的功能,我們寫(xiě)出來(lái)的代碼會(huì)更好。 這也會(huì)讓我們的代碼更具可讀性。 我們?cè)僖膊挥脫?dān)心方法是不是綁定了 `this`,也不必記住 `this` 之間的關(guān)系等等。 我們可以專心寫(xiě)代碼了。

React State Hook

狀態(tài),是React生態(tài)系統(tǒng)的基礎(chǔ)。接下來(lái)我將通過(guò)介紹最常見(jiàn)的 Hook —— `useState()` 來(lái)讓大家初步了解 Hooks。

我們先來(lái)看一下具有狀態(tài)的類組件。

import React, { Component } from 'react';
import './styles.css';

class Counter extends Component {
 state = {
  count: this.props.initialValue,
 };

 setCount = () => {
  this.setState({ count: this.state.count + 1 });
 };

 render() {
  return (
   <div>
    <h2>This is a counter using a class</h2>
    <h1>{this.state.count}</h1>

    <button onClick={this.setCount}>Click to Increment</button>
   </div>
  );
 }
}

export default Counter;

有了React Hooks 之后,我們可以重寫(xiě)這個(gè)類組件并刪除很多內(nèi)容,使其更易理解

import React, { useState } from 'react';

function CounterWithHooks(props) {
 const [count, setCount] = useState(props.initialValue);

 return (
  <div>
   <h2>This is a counter using hooks</h2>
   <h1>{count}</h1>
   <button onClick={() => setCount(count + 1)}>Click to Increment</button>
  </div>
 );
}

export default CounterWithHooks;

代碼變少了,但這到底是啥情況呢?

React State Syntax

在上面的代碼里,我們已經(jīng)用到了人生中第一個(gè) React Hook

const [count, setCount] = useState();

簡(jiǎn)單來(lái)講,這里使用了數(shù)組的結(jié)構(gòu)賦值。

useState()函數(shù)為我們提供了兩個(gè)東西:

* 一個(gè)保存狀態(tài)值的變量,在本例中稱為count;
* 一個(gè)更改值的函數(shù),在本例中稱為setCount。

當(dāng)然,你可以為這兩個(gè)東西起任何你想要的名字。

const [myCount, setCount] = useState(0);

而且,你也可以在你的代碼中像使用正常變量/函數(shù)一樣去用他們。

function CounterWithHooks() {
 const [count, setCount] = useState();

 return (
  <div>
   <h2>This is a counter using hooks</h2>
   <h1>{count}</h1>
   <button onClick={() => setCount(count + 1)}>Click to Increment</button>
  </div>
 );
}

注意最上面的的`useState` Hook。 我們聲明、結(jié)構(gòu)了2個(gè)東西:

* counter:是用來(lái)保存狀態(tài)的
* setCounter:是用來(lái)更改計(jì)數(shù)器變量的函數(shù)

往下看代碼,您會(huì)看到這一行:

<h1>{count}</h1>


這是一個(gè)使用 Hooks 變量的例子。在JSX中,我們將 `count` 變量放在 `{}` 中,然后作為JavaScript執(zhí)行它,最后將 `count` 的值將展示在頁(yè)面上。

我們來(lái)對(duì)照一下我們之前在類組件中使用狀態(tài)的方式:

<h1>{this.state.count}</h1>

你會(huì)發(fā)現(xiàn),我么再也不需要關(guān)注 `this` 的使用了,這使我們的編碼工作變得更加輕松了。

比如,當(dāng)你沒(méi)定義 {count}的時(shí)候,VS Code編輯器直接就給你報(bào)警告了,你就更早的發(fā)現(xiàn)錯(cuò)誤。 但是在運(yùn)行代碼之前,VS Code 可不會(huì)知道 {this.state.count} 是不是定義了。

我們繼續(xù)往下看

<button onClick={() => setCount(count + 1)}>Click to Increment</button>

在這行代碼中,我們使用 setCount函數(shù)來(lái)更改 count變量。

單擊按鈕的時(shí)侯,我們把 count變量更新為1。由于狀態(tài)變化,因此會(huì)觸發(fā)視圖重新渲染,React 會(huì)用新的 count值為我們更新視圖。 真香!

那我怎么給數(shù)據(jù)一個(gè)初始值呢?

您可以通過(guò)給`useState()`傳遞參數(shù)來(lái)設(shè)置初始狀態(tài)。 可以是一個(gè)硬編碼的值:

 const [count, setCount] = useState(0);

或者你也可以用 `props` 傳進(jìn)來(lái)的值作為初始值:

const [count, setCount] = useState(props.initialValue);

不論你的props.initialValue是啥,都會(huì)賦值給count作為初始值。

總結(jié)一下:useState最爽的地方在于,你可以像使用正常變量、函數(shù)一樣處理你的狀態(tài)。


那如果我有多個(gè)狀態(tài)數(shù)據(jù)咋辦?

這是就是Hooks另外一個(gè)牛逼的地方了,在組件里,`useState`你想用多少次,就用多少次:


const [count, setCount] = useState(props.initialValue);

const [title, setTitle] = useState("This is my title");

const [age, setAge] = useState(25);

如你所見(jiàn),我們現(xiàn)在有3個(gè)獨(dú)立的狀態(tài)對(duì)象。例如,如果我們想更新年齡,只需調(diào)用`setAge()`函數(shù)。`count`和`title`也是一樣。我們不再受制于舊的笨重的類組件方式——用`setState()`來(lái)管理一個(gè)超大的狀態(tài)對(duì)象。

this.setState({ count: props.initialValue, title: "This is my title", age: 25 })

那數(shù)據(jù)更新的時(shí)候,我要做一些事情怎么做?

在使用函數(shù)組件 + React Hooks 這種模式下,我們?cè)僖膊挥萌ス苁裁瓷芷诹?,什?`componentDidMount`、`componentDidUpdate`都可以統(tǒng)統(tǒng)見(jiàn)鬼去了。

你可能會(huì)問(wèn),那我用啥???別慌,兄弟! React 給我們提供了另外一個(gè)鉤子來(lái)干這事兒。

useEffect

效果鉤子useEffect是我們處理“副作用”的地方。

呃,副作用?那是啥? 

副作用

那我們就先偏離一下正題,討論一下副作用到底是什么。這有助于我們理解 `useEffect()` 的作用以及為什么他很有用。

一個(gè)你看不懂的無(wú)聊的正規(guī)解釋?xiě)?yīng)該是:

“在編程中,副作用是指在程序處理過(guò)程改變了程序范圍之外的變量”。

用 React 術(shù)語(yǔ)來(lái)說(shuō),副作用其實(shí)意味著“當(dāng)組件的變量或狀態(tài)因某些外部事物而改變”。 例如:

·組件接受了一個(gè)改變組件本身狀態(tài)的props
·當(dāng)組件進(jìn)行接口調(diào)用并在接口返回結(jié)果是執(zhí)行了某些操作(例如,更改了組件的狀態(tài))

那么為什么稱之為副作用呢? 

我們不能確定這些代碼的執(zhí)行結(jié)果是什么。 我們永遠(yuǎn)無(wú)法百分百確定我們的組件會(huì)接收到什么樣的 `props `,也無(wú)法確定接口調(diào)用返回的結(jié)果數(shù)據(jù)是什么。 而且,我們無(wú)法確定這將如何影響我們的組件。

當(dāng)然,我們也可以編寫(xiě)代碼校驗(yàn)、處理錯(cuò)誤等,但是我們最終還是不能確定這樣的事情帶來(lái)的副作用是什么。

所以可以這么說(shuō),當(dāng)組件的狀態(tài)因?yàn)橐恍┩饨缫蛩馗淖兊臅r(shí)候,這就可以稱作副作用。

好了,我們可以回正題了。繼續(xù)來(lái)看`useEffect`這個(gè)Hook。

在使用函數(shù)組件時(shí),我們不再使用生命周期鉤子函數(shù),例如 `componentDidMount`,`componentDidUpdate`等。因此,可以這么說(shuō),`useEffect` Hook 代替了之前我們用到的React鉤子函數(shù)。

讓我們比較一下基于類的組件和`useEffect` Hooks的使用方式

import React, { Component } from 'react';

class App extends Component {
 componentDidMount() {
  console.log('I have just mounted!');
 }

 render() {
  return <div>Insert JSX here</div>;
 }
}


用了`useEffect`之后

function App() {
 useEffect(() => {
  console.log('I have just mounted!');
 });

 return <div>Insert JSX here</div>;
}

在繼續(xù)往下之前,我們必須要知道一件事兒,在默認(rèn)情況下,`useEffect` Hook 在每次渲染和重新渲染時(shí)都會(huì)執(zhí)行。 

因此,只要組件中的狀態(tài)發(fā)生變化或組件收到新的`props`時(shí),組件都會(huì)重新渲染并導(dǎo)致`useEffect Hook 再次運(yùn)行。


能不能只執(zhí)行一次useEffect  (就像 componentDidMount 一樣)

如果 `useEffect` Hook 在組件每次渲染時(shí)都運(yùn)行,那么我們?cè)趺床拍茏龅?Hook 在掛載組件時(shí)僅運(yùn)行一次? 

例如,如果組件從接口獲取數(shù)據(jù),我們肯定不希望每次重新渲染組件時(shí)都去重新請(qǐng)求下數(shù)據(jù)吧?

`useEffect()` 鉤子接受第二個(gè)參數(shù),是一個(gè)數(shù)組,其中包含導(dǎo)致 `useEffect` 鉤子運(yùn)行的依賴項(xiàng)的列表。當(dāng)這些依賴項(xiàng)更改時(shí),它將觸發(fā) Effect Hook。如果想要只運(yùn)行一次 Effect Hook,那直接給他傳遞一個(gè)空數(shù)組,就OK啦??!

useEffect(() => {
 console.log('This only runs once');
}, []);

這就意味著 useEffect Hook 將在第一次渲染時(shí)正常運(yùn)行。然而,當(dāng)你的組件重新渲染時(shí),useEffect 會(huì)想 “好吧,我已經(jīng)運(yùn)行了,數(shù)組中啥也沒(méi)有,我沒(méi)啥依賴項(xiàng),誰(shuí)變都跟我沒(méi)關(guān)系了,所以我不必再運(yùn)行了。” 然后就什么也不做了。

總結(jié): 空數(shù)組就意味著useEffect Hook只在掛載時(shí)運(yùn)行一次。

當(dāng)有內(nèi)容更新時(shí)使用effect (就像componentDidUpdate一樣)

我們已經(jīng)介紹了如何確保 useEffect Hook僅運(yùn)行一次,但是當(dāng)我們的組件收到新的 `props` 時(shí)該怎么辦? 或者我們要在狀態(tài)更改時(shí)運(yùn)行一些代碼? 其實(shí)Hooks 也能處理!

useEffect(() => {
     console.log("The name props has changed!")
 }, [props.name]);

請(qǐng)注意,這次我們?nèi)绾螌|西傳遞給useEffect數(shù)組的,`props.name`。

在這種情況下,useEffect Hook 將像往常一樣在首次加載時(shí)運(yùn)行。 每當(dāng)您的組件從其父組件收到新的`props.name`時(shí),都會(huì)觸發(fā)useEffect Hook,并且運(yùn)行其中的代碼。

我們也可以使用狀態(tài)變量來(lái)做同樣的事情:

const [name, setName] = useState("Chris");

 useEffect(() => {
    console.log("The name state variable has changed!");
 }, [name]);

每當(dāng)`name`發(fā)生變化時(shí),組件就會(huì)重新渲染 ,useEffect Hook 就會(huì)運(yùn)行并輸出消息。而且因?yàn)檫@是一個(gè)數(shù)組,我們其實(shí)可以向它添加多個(gè)東西:

const [name, setName] = useState("Chris");

 useEffect(() => {
    console.log("Something has changed!");
 }, [name, props.name]);

這樣,當(dāng) `name` 狀態(tài)變量更改或 `props.name` 更改時(shí),useEffect Hook 都將運(yùn)行并顯示控制臺(tái)消息。

那我們能用componentWillUnmount嗎?

想要在組件即將卸載時(shí)運(yùn)行一個(gè)Hook,我們只需從`useEffect` Hook 返回一個(gè)函數(shù)

useEffect(() => {

 console.log('running effect');

 return () => {
  console.log('unmounting');
 };
});


那我們可以多個(gè)不同的 Hooks 一起使用嗎?

當(dāng)然! 你可以在組件中使用任意數(shù)量的Hooks,并根據(jù)需要混合使用

function App = () => {
    const [name, setName] = useState();
    const [age, setAge] = useState();

 useEffect(()=>{
    console.log("component has changed");
 }, [name, age])

 return(
    <div>Some jsx here...<div>
 )
}


小結(jié)- 接下來(lái)干啥呢?

你這不已經(jīng)學(xué)會(huì)React Hooks了么,Hooks允許我們使用老式的JavaScript函數(shù)來(lái)創(chuàng)建更簡(jiǎn)單的React組件,并減少大量代碼。

接下來(lái),手不癢癢么?當(dāng)然是自己趕緊動(dòng)手那Hooks做項(xiàng)目體驗(yàn)去??!


猜你喜歡

JavaScript中如何搜索數(shù)組元素? 

JavaScript中Math對(duì)象常用屬性和方法詳細(xì)介紹



分享到:
在線咨詢 我要報(bào)名
和我們?cè)诰€交談!