-
React Hooks
的設(shè)計目的兰迫,就是加強版函數(shù)組件,完全不使用"類"漾唉,就能寫出一個全功能的組件。
Hook 這個單詞的意思是"鉤子"堰塌。
React Hooks 的意思是赵刑,組件盡量寫成純函數(shù),如果需要外部功能和副作用场刑,就用鉤子把外部代碼"鉤"進來般此。 React Hooks 就是那些鉤子。
你需要什么功能牵现,就使用什么鉤子铐懊。React 默認(rèn)提供了一些常用鉤子,你也可以封裝自己的鉤子瞎疼。
所有的鉤子都是為函數(shù)引入外部功能科乎,所以 React 約定,鉤子一律使用use前綴命名贼急,便于識別茅茂。你要使用 xxx 功能,鉤子就命名為 usexxx太抓。
下面介紹 React 默認(rèn)提供的四個最常用的鉤子
useState()
useContext()
useReducer()
useEffect()
useState():狀態(tài)鉤子
useState()
用于為函數(shù)組件引入狀態(tài)(state
)空闲。純函數(shù)不能有狀態(tài),所以把狀態(tài)放在鉤子里面腻异。
import React, { useState } from "react";
export default function Button() {
const [buttonText, setButtonText] = useState("Click me, please");
function handleClick() {
return setButtonText("Thanks, been clicked!");
}
return <button onClick={handleClick}>{buttonText}</button>;
}
上面代碼中进副,Button
組件是一個函數(shù),內(nèi)部使用useState()
鉤子引入狀態(tài)悔常。
useState()
這個函數(shù)接受狀態(tài)的初始值影斑,作為參數(shù),上例的初始值為按鈕的文字机打。該函數(shù)返回一個數(shù)組矫户,數(shù)組的第一個成員是一個變量(上例是buttonText
),指向狀態(tài)的當(dāng)前值残邀。第二個成員是一個函數(shù)皆辽,用來更新狀態(tài),約定是set前綴加上狀態(tài)的變量名(上例是setButtonText
)芥挣。
useContext():共享狀態(tài)鉤子
如果需要在組件之間共享狀態(tài)驱闷,可以使用useContext()
。
現(xiàn)在有兩個組件 Navbar 和 Messages空免,我們希望它們之間共享狀態(tài)
<div className="App">
<Navbar/>
<Messages/>
</div>
第一步就是使用 React Context API空另,在組件外部建立一個 Context。
const AppContext = React.createContext({});
組件封裝代碼如下:
<AppContext.Provider value={{
username: 'superawesome'
}}>
<div className="App">
<Navbar/>
<Messages/>
</div>
</AppContext.Provider>
上面代碼中蹋砚,AppContext.Provider
提供了一個 Context
對象扼菠,這個對象可以被子組件共享
Navbar 組件的代碼如下
const Navbar = () => {
const { username } = useContext(AppContext);
return (
<div className="navbar">
<p>AwesomeSite</p>
<p>{username}</p>
</div>
);
}
上面代碼中摄杂,useContext()鉤子函數(shù)用來引入 Context 對象,從中獲取username屬性
Message 組件的代碼也類似:
const Messages = () => {
const { username } = useContext(AppContext)
return (
<div className="messages">
<h1>Messages</h1>
<p>1 message for {username}</p>
<p className="message">useContext is awesome!</p>
</div>
)
}
index.js:
import React, { useContext } from "react";
import ReactDOM from "react-dom";
import "./styles.css";
const AppContext = React.createContext({});
const Navbar = () => {
const { username } = useContext(AppContext)
return (
<div className="navbar">
<p>AwesomeSite</p>
<p>{username}</p>
</div>
)
}
const Messages = () => {
const { username } = useContext(AppContext)
return (
<div className="messages">
<h1>Messages</h1>
<p>1 message for {username}</p>
<p className="message">useContext is awesome!</p>
</div>
)
}
function App() {
return (
<AppContext.Provider value={{
username: 'superawesome'
}}>
<div className="App">
<Navbar />
<Messages />
</div>
</AppContext.Provider>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
useReducer():action 鉤子
React 本身不提供狀態(tài)管理功能循榆,通常需要使用外部庫析恢。這方面最常用的庫是 Redux
Redux 的核心概念是,組件發(fā)出 action 與狀態(tài)管理器通信秧饮。狀態(tài)管理器收到 action 以后映挂,使用 Reducer 函數(shù)算出新的狀態(tài),Reducer 函數(shù)的形式是(state, action) => newState
useReducers()
鉤子用來引入 Reducer 功能
const [state, dispatch] = useReducer(reducer, initialState)
上面是useReducer()
的基本用法浦楣,它接受 Reducer 函數(shù)和狀態(tài)的初始值作為參數(shù)袖肥,返回一個數(shù)組。數(shù)組的第一個成員是狀態(tài)的當(dāng)前值振劳,第二個成員是發(fā)送 action
的dispatch
函數(shù)
下面是一個計數(shù)器的例子椎组。用于計算狀態(tài)的 Reducer 函數(shù)如下
const myReducer = (state, action) => {
switch(action.type) {
case('countUp'):
return {
...state,
count: state.count + 1
}
default:
return state;
}
}
組件代碼如下
function App() {
const [state, dispatch] = useReducer(myReducer, { count: 0 });
return (
<div className="App">
<button onClick={() => dispatch({ type: 'countUp' })}>
+1
</button>
<p>Count: {state.count}</p>
</div>
);
}
index.js
import React, { useReducer } from "react";
import ReactDOM from "react-dom";
import "./styles.css";
const myReducer = (state, action) => {
switch(action.type) {
case('countUp'):
return {
...state,
count: state.count + 1
}
default:
return state
}
}
function App() {
const [state, dispatch] = useReducer(myReducer, { count: 0 })
return (
<div className="App">
<button onClick={() => dispatch({ type: 'countUp' })}>
+1
</button>
<p>Count: {state.count}</p>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
由于 Hooks 可以提供共享狀態(tài)和 Reducer 函數(shù),所以它在這些方面可以取代 Redux历恐。但是寸癌,它沒法提供中間件(middleware)和時間旅行(time travel),如果你需要這兩個功能弱贼,還是要用 Redux
useEffect():副作用鉤子
useEffect()
用來引入具有副作用的操作蒸苇,最常見的就是向服務(wù)器請求數(shù)據(jù)。以前吮旅,放在componentDidMount
里面的代碼溪烤,現(xiàn)在可以放在useEffect()
useEffect()的用法如下:
useEffect(() => {
// Async Action
}, [dependencies])
上面用法中,useEffect()
接受兩個參數(shù)庇勃。第一個參數(shù)是一個函數(shù)檬嘀,異步操作的代碼放在里面。第二個參數(shù)是一個數(shù)組责嚷,用于給出 Effect 的依賴項鸳兽,只要這個數(shù)組發(fā)生變化,useEffect()
就會執(zhí)行罕拂。第二個參數(shù)可以省略揍异,這時每次組件渲染時,就會執(zhí)行useEffect()
const Person = ({ personId }) => {
const [loading, setLoading] = useState(true);
const [person, setPerson] = useState({});
useEffect(() => {
setLoading(true);
fetch(`https://swapi.co/api/people/${personId}/`)
.then(response => response.json())
.then(data => {
setPerson(data);
setLoading(false);
});
}, [personId])
if (loading === true) {
return <p>Loading ...</p>
}
return <div>
<p>You're viewing: {person.name}</p>
<p>Height: {person.height}</p>
<p>Mass: {person.mass}</p>
</div>
}
上面代碼中爆班,每當(dāng)組件參數(shù)personId發(fā)生變化衷掷,useEffect()就會執(zhí)行。組件第一次渲染時柿菩,useEffect()也會執(zhí)行
創(chuàng)建自己的 Hooks
上例的 Hooks 代碼還可以封裝起來戚嗅,變成一個自定義的 Hook,便于共享
Person 組件就改用這個新的鉤子,引入封裝的邏輯
import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
import "./styles.css";
const usePerson = personId => {
const [loading, setLoading] = useState(true);
const [person, setPerson] = useState({});
useEffect(() => {
setLoading(true);
fetch(`https://swapi.co/api/people/${personId}/`)
.then(response => response.json())
.then(data => {
setPerson(data);
setLoading(false);
});
}, [personId]);
return [loading, person];
};
const Person = ({ personId }) => {
const [loading, person] = usePerson(personId);
if (loading === true) {
return <p>Loading ...</p>;
}
return (
<div>
<p>You're viewing: {person.name}</p>
<p>Height: {person.height}</p>
<p>Mass: {person.mass}</p>
</div>
);
};
function App() {
const [show, setShow] = useState("1");
return (
<div className="App">
<Person personId={show} />
<div>
Show:
<button onClick={() => setShow("1")}>Luke</button>
<button onClick={() => setShow("2")}>C-3PO</button>
</div>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);