原文地址:React Hooks: everything you need to know! ??
從React 16.8.0開(kāi)始复唤,有新的方法可以優(yōu)雅地調(diào)用異步代碼藐石,從而更輕松地在組件之間重用邏輯亮航。
作為reactjs開(kāi)發(fā)人員萌狂,您有責(zé)任了解最新的react框架功能啄骇。不是為了取悅您的老板,而是要在該領(lǐng)域和市場(chǎng)中保持相關(guān)性灸促。
我仍然記得過(guò)去的美好時(shí)光异旧,當(dāng)時(shí)沒(méi)人在談?wù)搑edux模式,而我的react應(yīng)用程序是狀態(tài)混亂的(2014年中)佣谐。
最初引入flux模式時(shí)肚吏,它很難理解,實(shí)現(xiàn)起來(lái)似乎很復(fù)雜狭魂,但是幾年后罚攀,這是每個(gè)基于React Framework的項(xiàng)目中的標(biāo)準(zhǔn)。
與react hooks將發(fā)生的相同雌澄,是類(lèi)組件的替換和React框架的未來(lái)斋泄。
好的,這將是一篇漫長(zhǎng)的文章镐牺,所以我添加了一個(gè)目錄炫掐,以便您可以閱讀一些內(nèi)容,然后繼續(xù)進(jìn)行項(xiàng)目睬涧,然后在需要休息時(shí)再回來(lái)募胃。
我是唯一閱讀技術(shù)文章以清理思想旗唁,減輕日常工作壓力的人嗎?
內(nèi)容列表
- 什么是React hooks痹束?
- React Hook與React Class
- 現(xiàn)有的React hooks
- 表示含義
- useState hook
- useEffect hook
- useReducer hook
- useRef hook
- 關(guān)注點(diǎn)分離
- 預(yù)先使用案例
- 現(xiàn)實(shí)世界中的例子
- 顯示在線狀態(tài)
- 跟蹤地理位置
- 很棒的資源
- 結(jié)論
什么是React hooks检疫? ??
當(dāng)您使用Reactjs類(lèi)組件時(shí),可以使用狀態(tài)参袱,這就是為什么這些組件也稱(chēng)為有狀態(tài)的原因电谣,而且每個(gè)類(lèi)組件都有生命周期方法秽梅,例如:componentDidMount()抹蚀,componentDidUpdate()等。
您不能在函數(shù)組件中使用任何一種企垦。 函數(shù)組件不能使用自己的狀態(tài)环壤,也沒(méi)有生命周期方法。
現(xiàn)在钞诡,您可以使用React hooks了郑现。
React鉤子使我們能夠使用Reactjs功能組件并為其添加狀態(tài)和生命周期方法。
簡(jiǎn)而言之荧降,React鉤子是特殊函數(shù)接箫,可以擴(kuò)展功能組件的函數(shù),并使其具有生命周期事件和管理狀態(tài)的可能性朵诫。
讓我們比較一下使用React鉤子時(shí)類(lèi)與功能組件的不同之處辛友。
基于類(lèi)的方式良好的舊的流行寫(xiě)法
import React from 'react';
class ClickCounter extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0 // Initial value for our counter
};
}
setCount(numb) {
this.setState({
count: numb
})
}
render() {
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => this.setCount(this.state.count + 1).bind(this)}>
Click me
</button>
</div>
);
}
}
使用React hooks
import React, { useState } from 'react';
function ClickCounter() {
/**
useState creates a "count" variable that will store the state and a "setCount" function that will mute the "count" variable state.
**/
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
使用useState hook將狀態(tài)存儲(chǔ)在函數(shù)組件中的示例
更少的代碼行可以完成相同的工作!
不僅如此剪返,借助React鉤子废累,您現(xiàn)在可以重用狀態(tài)邏輯并更好地分離關(guān)注點(diǎn)。
剛開(kāi)始脱盲,這個(gè)新的API可能對(duì)您來(lái)說(shuō)很奇怪邑滨,但繼續(xù)與我一起,您將學(xué)習(xí)如何充分利用它钱反。
現(xiàn)有的React hooks ??
新的API帶有兩個(gè)主要的預(yù)先存在的鉤子掖看,還有一些用于其他用例
基本React hooks
所有React鉤子的基礎(chǔ),您將看到的所有其他鉤子都是這三個(gè)鉤子的變體面哥,或者將它們用作基本體哎壳。
- useState是狀態(tài)鉤子,用于在組件中聲明狀態(tài)
- useEffect是副作用掛鉤幢竹,用于將其用于數(shù)據(jù)提取耳峦,手動(dòng)更改DOM等。
- useContext與Reactjs Context API結(jié)合使用焕毫。 當(dāng)React Context提供程序更新時(shí)蹲坷,此掛鉤將觸發(fā)具有最新上下文值的渲染驶乾。
先進(jìn)的React鉤子
這些是庫(kù)附帶的其他內(nèi)置React鉤子中最重要的。
useReducer是useState的替代方法循签,如果您具有復(fù)雜的狀態(tài)邏輯级乐,則應(yīng)該使用它,如果您對(duì)Redux熟悉的話县匠,會(huì)喜歡它风科。
useRef使用它來(lái)訪問(wèn)帶有可變r(jià)ef對(duì)象的DOM元素。 比ref屬性更有用
那些特殊的括號(hào)
您可能會(huì)問(wèn) const [age乞旦,setAge] = useState(24)的語(yǔ)法含義贼穆,但這只是解構(gòu)數(shù)組的新方法,下面讓我向您展示另一種方法兰粉。
const ageStateVariable = useState(24); // Returns a tuple or an array of length 2
const age = ageStateVariable[0]; // First item
const setAge = ageStateVariable[1]; // Second item
// ES6 way to do this
const [age, setAge] = useState(24);
我喜歡簡(jiǎn)單而優(yōu)雅的單行代碼故痊,不像使用python的人那么多,而且我絕對(duì)不喜歡像使用python的人一樣瘋狂的單行代碼
規(guī)則
- 切勿從循環(huán)玖姑,條件或嵌套函數(shù)內(nèi)部調(diào)用掛鉤
- 切勿從常規(guī)函數(shù)調(diào)用掛鉤
- 僅在函數(shù)組件或自定義hooks中調(diào)用它們
- Hooks應(yīng)位于組件的頂層
- Hooks可以調(diào)用其他Hooks
useState hook ??
最容易使用和理解所有的鉤子愕秫。 其目的是將狀態(tài)存儲(chǔ)在函數(shù)組件中。
嗯焰络,從技術(shù)上講戴甩,我們不是將狀態(tài)存儲(chǔ)在其中,而是將其連接到由底層React庫(kù)處理的狀態(tài)的字典(鍵值)中闪彼。 但是我們暫時(shí)不會(huì)深入了解這些細(xì)節(jié)
import React, { useState } from 'react';
function myAwesomeComponent () {
const [name, setName] = useState('John');
...
}
useState返回具有狀態(tài)持有者屬性和setter方法的元組甜孤。
您使用狀態(tài)的初始值調(diào)用useState。
要更新?tīng)顟B(tài)备蚓,請(qǐng)調(diào)用setName函數(shù)
useEffect hook ??
在React類(lèi)中课蔬,通常會(huì)在componentDidMount中設(shè)置一個(gè)訂閱,并在componentWillUnmount中對(duì)其進(jìn)行清理郊尝。
通過(guò)react hook useEffect二跋,我們通過(guò)返回一個(gè)清除或取消訂閱效果的函數(shù)來(lái)執(zhí)行此操作。
如果您使用過(guò)mobx流昏,這種模式可能會(huì)讓您感到熟悉扎即,這類(lèi)似于反應(yīng)。
useEffect(() => {
PlacesAPI.subscribeToPlaceNews(props.place.id, handlePlacesNews);
return () => {
PlacesAPI.unsubscribeFromPlaceNews(props.place.id, handlePlacesNews);
};
});
為什么我們從effect中返回一個(gè)函數(shù)况凉?
這是用于effect的可選擇的清理機(jī)制谚鄙。 每個(gè)effect都可能返回一個(gè)函數(shù),在之后執(zhí)行清除操作刁绒。
這使我們可以保持彼此之間添加和刪除訂閱的邏輯闷营。
useReducer hook ??
當(dāng)您具有復(fù)雜的狀態(tài)邏輯時(shí),最好使用reducer。 如果您熟悉Redux之類(lèi)的庫(kù)或flux模式傻盟,那么您將一眼就理解了速蕊。
基本上,在您使用reducer調(diào)度或觸發(fā)視圖中的某些操作的情況下娘赴,這些事件將由reducer監(jiān)聽(tīng)规哲,這些reducer具有內(nèi)部邏輯來(lái)更新?tīng)顟B(tài)所在的商店。 現(xiàn)在诽表,當(dāng)商店更新時(shí)唉锌,您的組件將重新渲染。
import React, { useReducer, useState } from 'react';
import produce from 'immer';
function reducer(state, action) {
switch (action.type) {
case 'toggle':
return produce(state, (draftState) => {
draftState[action.payload].isCompleted = !draftState[action.payload].isCompleted;
});
case 'add':
return produce(state, (draftState) => {
draftState.push({ label: action.payload });
});
default:
return state;
}
}
function Todo({ isCompleted, label, onChange }) {
return <p>
<label style={{
textDecoration: isCompleted && 'line-through'
}}>
<input
type="checkbox"
checked={isCompleted || false}
onChange={onChange}
/>
<span>{label}</span>
</label>
</p>
}
function TodoList() {
const todos = [
{ label: 'Do something' },
{ label: 'Buy dinner' }
];
const [state, dispatch] = useReducer(reducer, todos);
const [newTodo, setNewTodo] = useState('');
return <>
{state.map((todo, i) => (
<Todo
key={i}
{...todo}
onChange={() => dispatch({ type: 'toggle', payload: i })}
/>
))}
<input
type="text"
value={newTodo}
onChange={(e) => setNewTodo(e.target.value)}
/>
<button onClick={() => {
dispatch({ type: 'add', payload: newTodo });
setNewTodo('');
}}>
Add
</button>
</>;
}
export default TodoList;
useRef hook ??
Refs用于訪問(wèn)render函數(shù)中渲染后的React元素或DOM元素竿奏。 useRef hook返回一個(gè)可變的ref對(duì)象袄简,該對(duì)象的.current屬性已初始化為傳遞的參數(shù)initialValue。 使用非常簡(jiǎn)單
function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
// `current` points to the mounted text input element
inputEl.current.focus();
};
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}
關(guān)注點(diǎn)分離 ??
使用Hooks议双,您可以從組件中提取狀態(tài)邏輯痘番,以便可以對(duì)其進(jìn)行獨(dú)立測(cè)試和重用。
Hooks允許您重用狀態(tài)邏輯平痰,而無(wú)需更改組件層次結(jié)構(gòu)。
例如伍纫,組件可能在componentDidMount和componentDidUpdate中執(zhí)行某些數(shù)據(jù)獲取宗雇。
但是,同一componentDidMount方法也可能包含設(shè)置事件偵聽(tīng)器的無(wú)關(guān)邏輯莹规,并在componentWillUnmount中執(zhí)行清理赔蒲。
在一起變化的相互關(guān)聯(lián)的代碼被分開(kāi),但是完全不相關(guān)的代碼最終以單個(gè)方法組合在一起良漱。
import React from 'react';
import PlacesAPI from '../services/place';
class PlaceNewsWithCounter extends React.Component {
constructor(props) {
super(props);
this.handlePlacesNews = this.handlePlacesNews.bind(this);
this.state = { count: 0, currentEvent: null };
}
// Unrelated stateful logic
componentDidMount() {
document.title = `You clicked ${this.state.count} times`;
PlacesAPI.subscribeToPlaceNews(
this.props.place.id,
this.handlePlacesNews
);
}
componentDidUpdate() {
document.title = `You clicked ${this.state.count} times`;
}
componentWillUnmount() {
PlacesAPI.unsubscribeFromPlaceNews(
this.props.place.id,
this.handlePlacesNews
);
}
handlePlacesNews(place) {
this.setState({
currentEvent: place.currentEvent
});
}
...
}
使用React鉤子的更好方法
import React, { useState, useEffect } from 'react';
import PlacesAPI from '../services/place';
function PlaceNewsWithCounter() {
// Logic for counter here...
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `You clicked ${count} times`;
});
// Logic for place API here...
const [currentEvent, setCurrentEvent] = useState(null);
function handlePlacesNews(place) {
setCurrentEvent(place.currentEvent);
}
useEffect(() => {
PlacesAPI.subscribeToPlaceNews(props.place.id, handlePlacesNews);
return () => {
PlacesAPI.unsubscribeFromPlaceNews(props.place.id, handlePlacesNews);
};
});
return ...;
}
提前使用示例
[圖片上傳失敗...(image-353ab6-1602425962902)]
使用useEffect進(jìn)行數(shù)據(jù)提取
通過(guò)結(jié)合使用useEffect和useState舞虱,可以使用useEffect進(jìn)行API調(diào)用,并將空數(shù)組或?qū)ο笞鳛榈诙€(gè)參數(shù)傳遞母市,使其具有與componentDidMount相同的行為矾兜。
這里的關(guān)鍵是第二個(gè)參數(shù)。 如果您不提供空數(shù)組或?qū)ο笞鳛榈诙€(gè)參數(shù)患久,則將在每個(gè)渲染器上調(diào)用API調(diào)用椅寺,并且該調(diào)用實(shí)際上與componentDidUpdate相同
const [todo, setTodo] = useState(null);
const [id, setId] = useState(1);
useEffect(() => {
if (!id) {
return;
}
fetch(`https://jsonplaceholder.typicode.com/todos/${id}`)
.then(results => results.json())
.then(data => {
setTodo(data);
});
}, [id]); // Don't forget to add this!
通過(guò)將第二個(gè)參數(shù)傳遞給useEffect,我們將在id屬性更改時(shí)設(shè)置訂閱蒋失,從而重新觸發(fā)效果
如果相反返帕,我們只想在該組件掛載時(shí)進(jìn)行API調(diào)用
const [fullName, setFullName] = useState(null);
useEffect(() => {
fetch('https://randomuser.me/api/')
.then(results => results.json())
.then(data => {
const {name} = data.results[0];
setFullName(`${name.first} ${name.last}`);
});
}, []); // <-- Have to pass in [] here!