Hook簡(jiǎn)介
? Hook 是 React 16.8 的新增特性。它可以讓你在不編寫 class 的情況下使用 state 以及其他的 React 特性暑中。
import React, { useState, useEffect } from 'react';
function Example() {
// 聲明一個(gè)新的叫做 “count” 的 state 變量
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
注意
React 16.8.0 是第一個(gè)支持 Hook 的版本壹瘟。升級(jí)時(shí),請(qǐng)注意更新所有的 package鳄逾,包括 React DOM稻轨。 React Native 從 0.59 版本開始支持 Hook。
什么是Hook
Hook 是一些可以讓你在函數(shù)組件里“鉤入” React state 及生命周期等特性的函數(shù)雕凹。Hook 不能在 class 組件中使用 —— 這使得你不使用 class 也能使用 React殴俱。
例如,useState
是允許你在 React 函數(shù)組件中添加 state 的 Hook(不推薦把你已有的組件全部重寫枚抵,但是你可以在新組件里開始使用 Hook线欲。)
React 內(nèi)置了一些像 useState
這樣的 Hook。你也可以創(chuàng)建你自己的 Hook 來(lái)復(fù)用不同組件之間的狀態(tài)邏輯汽摹。我們會(huì)先介紹這些內(nèi)置的 Hook询筏。
什么時(shí)候我會(huì)用 Hook?
如果你在編寫函數(shù)組件并意識(shí)到需要向其添加一些 state竖慧,以前的做法是必須將其它轉(zhuǎn)化為 class∠犹祝現(xiàn)在你可以在現(xiàn)有的函數(shù)組件中使用 Hook逆屡。
引入Hook對(duì)于當(dāng)前項(xiàng)目的影響
沒有破壞性改動(dòng)
> 1. **完全可選的。** 你無(wú)需重寫任何已有代碼就可以在一些組件中嘗試 Hook踱讨。但是如果你不想魏蔗,你不必現(xiàn)在就去學(xué)習(xí)或使用 Hook。
> 2. **100% 向后兼容的痹筛。** Hook 不包含任何破壞性改動(dòng)莺治。
> 3. **現(xiàn)在可用。** Hook 已發(fā)布于 v16.8.0帚稠。
Hook 不會(huì)影響你對(duì) React 概念的理解谣旁。 恰恰相反,Hook 為已知的 React 概念提供了更直接的 API:props滋早, state榄审,context,refs 以及生命周期杆麸。稍后我們將看到搁进,Hook 還提供了一種更強(qiáng)大的方式來(lái)組合他們。
動(dòng)機(jī)
Hook 解決了編寫和維護(hù)組件中各種各樣看起來(lái)不相關(guān)的問(wèn)題昔头。無(wú)論你正在學(xué)習(xí) React饼问,或每天使用,或者更愿嘗試另一個(gè)和 React 有相似組件模型的框架揭斧,你都可能對(duì)這些問(wèn)題似曾相識(shí)莱革。
- 對(duì)于復(fù)雜組件中的處理優(yōu)勢(shì):
我們經(jīng)常維護(hù)一些組件,組件起初很簡(jiǎn)單讹开,但是逐漸會(huì)被狀態(tài)邏輯和副作用充斥盅视。每個(gè)生命周期常常包含一些不相關(guān)的邏輯。例如萧吠,組件常常在 componentDidMount
和 componentDidUpdate
中獲取數(shù)據(jù)左冬。但是桐筏,同一個(gè) componentDidMount
中可能也包含很多其它的邏輯纸型,如設(shè)置事件監(jiān)聽,而之后需在 componentWillUnmount
中清除梅忌。相互關(guān)聯(lián)且需要對(duì)照修改的代碼被進(jìn)行了拆分狰腌,而完全不相關(guān)的代碼卻在同一個(gè)方法中組合在一起。如此很容易產(chǎn)生 bug牧氮,并且導(dǎo)致邏輯不一致琼腔。
在多數(shù)情況下,不可能將組件拆分為更小的粒度踱葛,因?yàn)闋顟B(tài)邏輯無(wú)處不在丹莲。這也給測(cè)試帶來(lái)了一定挑戰(zhàn)光坝。同時(shí),這也是很多人將 React 與狀態(tài)管理庫(kù)結(jié)合使用的原因之一甥材。但是盯另,這往往會(huì)引入了很多抽象概念,需要你在不同的文件之間來(lái)回切換洲赵,使得復(fù)用變得更加困難鸳惯。
為了解決這個(gè)問(wèn)題,Hook 將組件中相互關(guān)聯(lián)的部分拆分成更小的函數(shù)(比如設(shè)置訂閱或請(qǐng)求數(shù)據(jù))叠萍,而并非強(qiáng)制按照生命周期劃分芝发。你還可以使用 reducer 來(lái)管理組件的內(nèi)部狀態(tài),使其更加可預(yù)測(cè)苛谷。
-
對(duì)于現(xiàn)在正在使用的class組件的替代
除了代碼復(fù)用和代碼管理會(huì)遇到困難外辅鲸,我們還發(fā)現(xiàn) class 是學(xué)習(xí) React 的一大屏障刀崖。你必須去理解 JavaScript 中
this
的工作方式赚爵,這與其他語(yǔ)言存在巨大差異。還不能忘記綁定事件處理器析藕。沒有穩(wěn)定的語(yǔ)法提案赫蛇,這些代碼非常冗余绵患。大家可以很好地理解 props,state 和自頂向下的數(shù)據(jù)流悟耘,但對(duì) class 卻一籌莫展落蝙。即便在有經(jīng)驗(yàn)的 React 開發(fā)者之間,對(duì)于函數(shù)組件與 class 組件的差異也存在分歧暂幼,甚至還要區(qū)分兩種組件的使用場(chǎng)景筏勒。當(dāng)然class也不會(huì)從React中移除。
useState
先看一個(gè)經(jīng)典的計(jì)數(shù)器例子:
import React, { useState } from 'react';
function Example() {
// 聲明一個(gè)叫 “count” 的 state 變量旺嬉。
const [count, setCount] = useState(0);
const [count1, setCount1] = useState(0);
const [count2, setCount2] = useState(0);
const [count3, setCount3] = useState(0);
let num = 0
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
在這里管行,useState
就是一個(gè) Hook 。通過(guò)在函數(shù)組件里調(diào)用它來(lái)給組件添加一些內(nèi)部 state邪媳。React 會(huì)在重復(fù)渲染時(shí)保留這個(gè) state捐顷。useState
會(huì)返回一對(duì)值:當(dāng)前狀態(tài)和一個(gè)讓你更新它的函數(shù),你可以在事件處理函數(shù)中或其他一些地方調(diào)用這個(gè)函數(shù)雨效。它類似 class 組件的 this.setState
迅涮,但是它不會(huì)把新的 state 和舊的 state 進(jìn)行合并。
useState
唯一的參數(shù)就是初始 state徽龟。在上面的例子中叮姑,我們的計(jì)數(shù)器是從零開始的,所以初始 state 就是 0
据悔。值得注意的是传透,不同于 this.state
耘沼,這里的 state 不一定要是一個(gè)對(duì)象 —— 如果你有需要,它也可以是朱盐。這個(gè)初始 state 參數(shù)只有在第一次渲染時(shí)會(huì)被用到耕拷。
聲明多個(gè)State變量
可以在一個(gè)組件內(nèi)多次使用State Hook:
function ExampleWithManyStates() {
// 聲明多個(gè) state 變量!
const [age, setAge] = useState(42);
const [fruit, setFruit] = useState('banana');
const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);
// ...
}
數(shù)組解構(gòu)的語(yǔ)法讓我們?cè)谡{(diào)用 useState
時(shí)可以給 state 變量取不同的名字托享。當(dāng)然骚烧,這些名字并不是 useState
API 的一部分。React 假設(shè)當(dāng)你多次調(diào)用 useState
的時(shí)候闰围,你能保證每次渲染時(shí)它們的調(diào)用順序是不變的赃绊。后面我們會(huì)再次解釋它是如何工作的以及在什么場(chǎng)景下使用。
useState的使用
還是上面那個(gè)例子羡榴,下面我們寫一個(gè)的class的示例來(lái)對(duì)比一下:
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
}
render() {
return (
<div>
<p>You clicked {this.state.count} times</p>
<button onClick={() => this.setState({ count: this.state.count + 1 })}>
Click me
</button>
</div>
);
}
}
state 初始值為 { count: 0 }
碧查,當(dāng)用戶點(diǎn)擊按鈕后,我們通過(guò)調(diào)用 this.setState()
來(lái)增加 state.count
校仑。
Hook和函數(shù)組件
React的函數(shù)組件:
const Example = (props) => {
// 你可以在這使用 Hook
return <div />;
}
或者:
function Example(props) {
// 你可以在這使用 Hook
return <div />;
}
之前可能把它們叫做“無(wú)狀態(tài)組件”忠售。但現(xiàn)在我們?yōu)樗鼈円肓耸褂?React state 的能力,所以我們更喜歡叫它”函數(shù)組件”迄沫。
Hook 在 class 內(nèi)部是不起作用的稻扬。但你可以使用它們來(lái)取代 class 。
聲明State變量
在 class示例 中羊瘩,我們通過(guò)在構(gòu)造函數(shù)中設(shè)置 this.state
為 { count: 0 }
來(lái)初始化 count
state 為 0
.
在函數(shù)組件中泰佳,是沒有this
,所以不能分配或讀取this.state
尘吗。我們直接在組件中調(diào)用useState Hook:
import React, { useState } from 'react';
function Example() {
// 聲明一個(gè)叫 “count” 的 state 變量
const [count, setCount] = useState(0);
調(diào)用useState方法的時(shí)候做了什么逝她?
它定義一個(gè) “state 變量”。我們的變量叫 count
睬捶, 但是我們可以叫他任何名字黔宛,比如 banana
。這是一種在函數(shù)調(diào)用時(shí)保存變量的方式 —— useState
是一種新方法擒贸,它與 class 里面的 this.state
提供的功能完全相同臀晃。一般來(lái)說(shuō),在函數(shù)退出后變量就就會(huì)”消失”酗宋,而 state 中的變量會(huì)被 React 保留积仗。
useState
需要哪些參數(shù)疆拘?
useState()
方法里面唯一的參數(shù)就是初始 state蜕猫。不同于 class 的是,我們可以按照需要使用數(shù)字或字符串對(duì)其進(jìn)行賦值哎迄,而不一定是對(duì)象回右。在示例中隆圆,只需使用數(shù)字來(lái)記錄用戶點(diǎn)擊次數(shù),所以我們傳了 0
作為變量的初始 state翔烁。(如果我們想要在 state 中存儲(chǔ)兩個(gè)不同的變量渺氧,只需調(diào)用 useState()
兩次即可。)
seState
方法的返回值是什么蹬屹?
返回值為:當(dāng)前 state 以及更新 state 的函數(shù)侣背。這就是我們寫 const [count, setCount] = useState()
的原因。這與 class 里面 this.state.count
和 this.setState
類似慨默,唯一區(qū)別就是你需要成對(duì)的獲取它們贩耐。
讀取State
當(dāng)我們想在 class 中顯示當(dāng)前的 count,我們讀取 this.state.count
:
<p>You clicked {this.state.count} times</p>
在函數(shù)中厦取,我們可以直接用 count
:
<p>You clicked {count} times</p>
更新State
在 class 中潮太,我們需要調(diào)用 this.setState()
來(lái)更新 count
值:
<button onClick={() => this.setState({ count: this.state.count + 1 })}>
Click me
</button>
在函數(shù)中,我們已經(jīng)有了 setCount
和 count
變量虾攻,所以我們不需要 this
:
<button onClick={() => setCount(count + 1)}>
Click me
</button>
useEffect
之前可能已經(jīng)在 React 組件中執(zhí)行過(guò)數(shù)據(jù)獲取铡买、訂閱或者手動(dòng)修改過(guò) DOM。我們統(tǒng)一把這些操作稱為“副作用”霎箍,或者簡(jiǎn)稱為“作用”奇钞。
useEffect
就是一個(gè) Effect Hook,給函數(shù)組件增加了操作副作用的能力漂坏。它跟 class 組件中的 componentDidMount
蛇券、componentDidUpdate
和 componentWillUnmount
具有相同的用途,只不過(guò)被合并成了一個(gè) API樊拓。
import React, { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
// 相當(dāng)于 componentDidMount 和 componentDidUpdate:
useEffect(() => {
// 使用瀏覽器的 API 更新頁(yè)面標(biāo)題
document.title = `You clicked ${count} times`;
});
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
當(dāng)你調(diào)用 useEffect
時(shí)纠亚,就是在告訴 React 在完成對(duì) DOM 的更改后運(yùn)行你的“副作用”函數(shù)。由于副作用函數(shù)是在組件內(nèi)聲明的筋夏,所以它們可以訪問(wèn)到組件的 props 和 state蒂胞。默認(rèn)情況下,React 會(huì)在每次渲染后調(diào)用副作用函數(shù) —— 包括第一次渲染的時(shí)候条篷。
數(shù)據(jù)獲取骗随,設(shè)置訂閱以及手動(dòng)更改 React 組件中的 DOM 都屬于副作用。不管你知不知道這些操作赴叹,或是“副作用”這個(gè)名字鸿染,應(yīng)該都在組件中使用過(guò)它們。
提示
如果你熟悉 React class 的生命周期函數(shù)乞巧,你可以把
useEffect
Hook 看做componentDidMount
涨椒,componentDidUpdate
和componentWillUnmount
這三個(gè)函數(shù)的組合。
在 React 組件中有兩種常見副作用操作:需要清除的和不需要清除的。我們來(lái)更仔細(xì)地看一下他們之間的區(qū)別蚕冬。
無(wú)需清除的 effect
有時(shí)候免猾,我們只想在 React 更新 DOM 之后運(yùn)行一些額外的代碼。比如發(fā)送網(wǎng)絡(luò)請(qǐng)求囤热,手動(dòng)變更 DOM猎提,記錄日志,這些都是常見的無(wú)需清除的操作旁蔼。因?yàn)槲覀冊(cè)趫?zhí)行完這些操作之后锨苏,就可以忽略他們了。讓我們對(duì)比一下使用 class 和 Hook 都是怎么實(shí)現(xiàn)這些副作用的
class示例:
在 React 的 class 組件中棺聊,render
函數(shù)是不應(yīng)該有任何副作用的蚓炬。一般來(lái)說(shuō),在這里執(zhí)行操作太早了躺屁,我們基本上都希望在 React 更新 DOM 之后才執(zhí)行我們的操作肯夏。
這就是為什么在 React class 中,我們把副作用操作放到 componentDidMount
和 componentDidUpdate
函數(shù)中犀暑⊙被鳎回到示例中,這是一個(gè) React 計(jì)數(shù)器的 class 組件耐亏。它在 React 對(duì) DOM 進(jìn)行操作之后徊都,立即更新了 document 的 title 屬性
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
}
componentDidMount() {
document.title = `You clicked ${this.state.count} times`;
}
componentDidUpdate() {
document.title = `You clicked ${this.state.count} times`;
}
render() {
return (
<div>
<p>You clicked {this.state.count} times</p>
<button onClick={() => this.setState({ count: this.state.count + 1 })}>
Click me
</button>
</div>
);
}
}
注意,在這個(gè) class 中广辰,我們需要在兩個(gè)生命周期函數(shù)中編寫重復(fù)的代碼暇矫。
這是因?yàn)楹芏嗲闆r下,我們希望在組件加載和更新時(shí)執(zhí)行同樣的操作择吊。從概念上說(shuō)李根,我們希望它在每次渲染之后執(zhí)行 —— 但 React 的 class 組件沒有提供這樣的方法。即使我們提取出一個(gè)方法几睛,我們還是要在兩個(gè)地方調(diào)用它房轿。
如何使用 useEffect
執(zhí)行相同的操作。
import React, { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `You clicked ${count} times`;
});
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
useEffect
做了什么所森?
通過(guò)使用這個(gè) Hook囱持,你可以告訴 React 組件需要在渲染后執(zhí)行某些操作。React 會(huì)保存你傳遞的函數(shù)(我們將它稱之為 “effect”)焕济,并且在執(zhí)行 DOM 更新之后調(diào)用它纷妆。在這個(gè) effect 中,我們?cè)O(shè)置了 document 的 title 屬性晴弃,不過(guò)我們也可以執(zhí)行數(shù)據(jù)獲取或調(diào)用其他命令式的 API掩幢。
為什么在組件內(nèi)部調(diào)用 useEffect
逊拍?
將 useEffect
放在組件內(nèi)部讓我們可以在 effect 中直接訪問(wèn) count
state 變量(或其他 props)。我們不需要特殊的 API 來(lái)讀取它 —— 它已經(jīng)保存在函數(shù)作用域中粒蜈。Hook 使用了 JavaScript 的閉包機(jī)制,而不用在 JavaScript 已經(jīng)提供了解決方案的情況下旗国,還引入特定的 React API
useEffect
會(huì)在每次渲染后都執(zhí)行嗎枯怖?
是的,默認(rèn)情況下能曾,它在第一次渲染之后和每次更新之后都會(huì)執(zhí)行度硝。(我們稍后會(huì)談到如何控制它。)你可能會(huì)更容易接受 effect 發(fā)生在“渲染之后”這種概念寿冕,不用再去考慮“掛載”還是“更新”蕊程。React 保證了每次運(yùn)行 effect 的同時(shí),DOM 都已經(jīng)更新完畢驼唱。
提示
與
componentDidMount
或componentDidUpdate
不同藻茂,使用useEffect
調(diào)度的 effect 不會(huì)阻塞瀏覽器更新屏幕,這讓你的應(yīng)用看起來(lái)響應(yīng)更快玫恳。大多數(shù)情況下辨赐,effect 不需要同步地執(zhí)行。在個(gè)別情況下(例如測(cè)量布局)京办,有單獨(dú)的useLayoutEffect
Hook 供你使用掀序,其 API 與useEffect
相同。
通過(guò)跳過(guò)effect進(jìn)行性能優(yōu)化
在 class 組件中惭婿,我們可以通過(guò)在 componentDidUpdate
中添加對(duì) prevProps
或 prevState
的比較邏輯解決:
componentDidUpdate(prevProps, prevState) {
if (prevState.count !== this.state.count) {
document.title = `You clicked ${this.state.count} times`;
}
}
這是很常見的需求不恭,所以它被內(nèi)置到了 useEffect
的 Hook API 中。如果某些特定值在兩次重渲染之間沒有發(fā)生變化财饥,你可以通知 React 跳過(guò)對(duì) effect 的調(diào)用换吧,只要傳遞數(shù)組作為 useEffect
的第二個(gè)可選參數(shù)即可:
useEffect(() => {
document.title = `You clicked ${count} times`;
}, [count]); // 僅在 count 更改時(shí)更新
上面這個(gè)示例中,我們傳入 [count]
作為第二個(gè)參數(shù)钥星。這個(gè)參數(shù)是什么作用呢式散?如果 count
的值是 5
,而且我們的組件重渲染的時(shí)候 count
還是等于 5
打颤,React 將對(duì)前一次渲染的 [5]
和后一次渲染的 [5]
進(jìn)行比較暴拄。因?yàn)閿?shù)組中的所有元素都是相等的(5 === 5
),React 會(huì)跳過(guò)這個(gè) effect编饺,這就實(shí)現(xiàn)了性能的優(yōu)化乖篷。
Hook的使用規(guī)則
Hook 就是 JavaScript 函數(shù),但是使用它們會(huì)有兩個(gè)額外的規(guī)則:
- 只能在函數(shù)最外層調(diào)用 Hook透且。不要在循環(huán)撕蔼、條件判斷或者子函數(shù)中調(diào)用豁鲤。
- 只能在 React 的函數(shù)組件中調(diào)用 Hook。不要在其他 JavaScript 函數(shù)中調(diào)用鲸沮。
使用hook獲取數(shù)據(jù)
import React, { useState, useEffect } from 'react';
import axios from 'axios';
function App() {
const [data, setData] = useState({ hits: [] });
useEffect(async () => {
const result = await axios(
'https://hn.algolia.com/api/v1/search?query=redux',
);
setData(result.data);
});
return (
<ul>
{data.hits.map(item => (
<li key={item.objectID}>
<a href={item.url}>{item.title}</a>
</li>
))}
</ul>
);
}
export default App;
這里我們使用 useEffect 的 effect hook 來(lái)獲取數(shù)據(jù)琳骡。并且使用 useState 中的 setData 來(lái)更新組件狀態(tài)。
但是如上代碼運(yùn)行的時(shí)候讼溺,你會(huì)發(fā)現(xiàn)一個(gè)特別煩人的循環(huán)問(wèn)題楣号。effect hook 的觸發(fā)不僅僅是在組件第一次加載的時(shí)候,還有在每一次更新的時(shí)候也會(huì)觸發(fā)怒坯。由于我們?cè)讷@取到數(shù)據(jù)后就進(jìn)行設(shè)置了組件狀態(tài)炫狱,然后又觸發(fā)了 effect hook。所以就會(huì)出現(xiàn)死循環(huán)剔猿。很顯然视译,這是一個(gè) bug!我們只想在組件第一次加載的時(shí)候獲取數(shù)據(jù) 归敬,這也就是為什么你可以提供一個(gè)空數(shù)組作為 useEffect
的第二個(gè)參數(shù)以避免在組件更新的時(shí)候也觸它酷含。當(dāng)然,這樣的話汪茧,也就是在組件加載的時(shí)候觸發(fā)第美。
解決方法: 可以在useEffect(()=>{}, [])來(lái)解決。
還有一個(gè)陷阱是會(huì)報(bào)一個(gè)Warning: useEffect function must return a cleanup function or nothing. Promises and useEffect(async () => …) are not supported, but you can call an async function inside an effect.. ``警告
代碼里面陆爽,我們使用 async/await
去獲取第三方的 API 的接口數(shù)據(jù)什往,根據(jù)文檔,每一個(gè) async
都會(huì)返回一個(gè) promise:async
函數(shù)聲明定義了一個(gè)異步函數(shù)慌闭,它返回一個(gè) AsyncFunction 對(duì)象别威。異步函數(shù)是通過(guò)事件循環(huán)異步操作的函數(shù),使用隱式的 Promise 返回結(jié)果然而驴剔,effect hook 不應(yīng)該返回任何內(nèi)容省古,或者清除功能
解決方法
import React, { useState, useEffect } from 'react';
import axios from 'axios';
function App() {
const [data, setData] = useState({ hits: [] });
useEffect(() => {
const fetchData = async () => {
const result = await axios(
'https://hn.algolia.com/api/v1/search?query=redux',
);
setData(result.data);
};
fetchData();
}, []);
return (
<ul>
{data.hits.map(item => (
<li key={item.objectID}>
<a href={item.url}>{item.title}</a>
</li>
))}
</ul>
);
}
export default App;
如何手動(dòng)觸發(fā)hook
**
function App() {
const [data, setData] = useState({ hits: [] });
const [query, setQuery] = useState('redux');
useEffect(() => {
const fetchData = async () => {
const result = await axios(
`http://hn.algolia.com/api/v1/search?query=${query}`,
);
setData(result.data);
};
fetchData();
}, []);
return (
...
);
}
export default App;
當(dāng)你嘗試輸入字段鍵入內(nèi)容的時(shí)候,他是不會(huì)再去觸發(fā)請(qǐng)求的丧失。因?yàn)槟闾峁┑氖且粋€(gè)空數(shù)組作為useEffect
的第二個(gè)參數(shù)是一個(gè)空數(shù)組豺妓,所以effect hook 的觸發(fā)不依賴任何變量,因此只在組件第一次加載的時(shí)候觸發(fā)布讹。所以這里我們希望當(dāng) query 這個(gè)字段一改變的時(shí)候就觸發(fā)搜索
把 query
作為第二個(gè)參數(shù)傳遞給了 effect hook琳拭,這樣的話,每當(dāng) query 改變的時(shí)候就會(huì)觸發(fā)搜索描验。
但是白嘁,這樣就會(huì)出現(xiàn)了另一個(gè)問(wèn)題:每一次的query 的字段變動(dòng)都會(huì)觸發(fā)搜索。如何提供一個(gè)按鈕來(lái)觸發(fā)請(qǐng)求呢膘流?
function App() {
const [data, setData] = useState({ hits: [] });
const [query, setQuery] = useState('redux');
const [search, setSearch] = useState('redux');
useEffect(() => {
const fetchData = async () => {
const result = await axios(
`http://hn.algolia.com/api/v1/search?query=${search}`,
);
setData(result.data);
};
fetchData();
}, [search]);
return (
<Fragment>
<input
type="text"
value={query}
onChange={event => setQuery(event.target.value)}
/>
<button type="button" onClick={() => setSearch(query)}>
Search
</button>
<ul>
{data.hits.map(item => (
<li key={item.objectID}>
<a href={item.url}>{item.title}</a>
</li>
))}
</ul>
</Fragment>
);
}
搜索的狀態(tài)設(shè)置為組件的初始化狀態(tài)絮缅,組件加載的時(shí)候就要觸發(fā)搜索鲁沥,類似的查詢和搜索狀態(tài)易造成混淆,為什么不把實(shí)際的 URL 設(shè)置為狀態(tài)而不是搜索狀態(tài)呢耕魄?
function App() {
const [data, setData] = useState({ hits: [] });
const [query, setQuery] = useState('redux');
const [url, setUrl] = useState(
'https://hn.algolia.com/api/v1/search?query=redux',
);
useEffect(() => {
const fetchData = async () => {
const result = await axios(url);
setData(result.data);
};
fetchData();
}, [url]);
return (
<Fragment>
<input
type="text"
value={query}
onChange={event => setQuery(event.target.value)}
/>
<button
type="button"
onClick={() =>
setUrl(`http://hn.algolia.com/api/v1/search?query=${query}`)
}
>
Search
</button>
<ul>
{data.hits.map(item => (
<li key={item.objectID}>
<a href={item.url}>{item.title}</a>
</li>
))}
</ul>
</Fragment>
);
}