最近在新項目中用到了react hooks蛔趴,趁熱乎總結(jié)一下嚣崭。
1. React Hooks是什么&為什么要用React Hooks?
以下引入官方文檔中的簡單介紹:Hook是React16.8的新特性,它可以讓你在不編寫class的情況下使用state以及其他React特性宾尚。
新特定的出現(xiàn)總歸是要解決以往開發(fā)過程中存在的一些問題丙笋,它和之前的版本對比主要有以下優(yōu)勢:
1. 可以再非class的情況下使用更多React特性。(可以不用再使用class來寫react代碼啦)
2. 函數(shù)更加專一化煌贴。(一個函數(shù)只負(fù)責(zé)一塊邏輯的處理御板,而并非將很多不相關(guān)的邏輯都堆在一個函數(shù))
3. 復(fù)用組件的狀態(tài)邏輯。這也是它主要要解決的問題牛郑,但只是狀態(tài)邏輯的復(fù)用怠肋,會共享數(shù)據(jù)的處理邏輯,而非數(shù)據(jù)本身井濒。
2. React Hooks的分類
根據(jù)官方文檔的介紹,React Hooks主要有以下幾種hook:
-
基礎(chǔ)hook
- useState:返回一個state和一個更新state的函數(shù)列林,功能類似于class中的state和this.setState瑞你。
- useEffect: 接收一個包含命令式、且可能有副作用代碼的函數(shù)希痴。它主要可以實現(xiàn)componentDidmount者甲、componentWillUnmount、componentDidUpdate的功能砌创。同時也可以在我們自己封裝的hook中使用useEffect虏缸。
-
useContext: 接收一個context對象,并返回該context的當(dāng)前值嫩实。調(diào)用了 useContext 的組件總會在 context 值變化時重新渲染刽辙。
useContext(MyContext)只是相當(dāng)于<MyContext.Consumer>,只是能接收到context的值并訂閱其變化甲献。我們?nèi)匀恍枰谏蠈咏M件中用<MyContext.Provider> 來傳遞變化宰缤。
-
額外的hook
- useReducer: 它接收一個形如 (state, action) => newState 的 reducer,并返回當(dāng)前的 state 以及與其配套的 dispatch 方法晃洒。可提供類似redux的功能慨灭。
- useCallback:它接收一個回調(diào)函數(shù)和一個依賴數(shù)組作為參數(shù),返回一個緩存的回調(diào)函數(shù)球及。
- useMemo:它接收一個回調(diào)函數(shù)和一個依賴數(shù)組作為參數(shù)氧骤,返回一個緩存的值。
- useRef:返回一個可變的 ref 對象吃引,并且在組件的整個生命周期內(nèi)保持不變筹陵,即可跨周期來獲取數(shù)據(jù)刽锤。
- useImperativeHandle:可以在使用 ref 時自定義暴露給父組件的實例值。
- useLayoutEffect:如果在useEffect中會操作dom惶翻,那就用useLsyoutEffect來代替姑蓝。它里邊的回調(diào)函數(shù)會在dom更新完成之后執(zhí)行,但在瀏覽器繪制前運行完成吕粗。
- useDebugValue: 可在React 開發(fā)者工具中顯示自定義 hook 的標(biāo)簽纺荧。
3. 詳解React Hooks
(1) useState
const [count, setCount] = useState(0);其中0作為count的初始值,后續(xù)調(diào)用count來取值颅筋,調(diào)用setCount()來修改值宙暇。
- 可根據(jù)狀態(tài)之間的關(guān)聯(lián)性來選擇將其設(shè)置為一個狀態(tài)還是多個狀態(tài)。例如
const [name, setName] = useState('susan');
const [school, setSchool] = useState('school-one');
//由于name和school都屬于個人信息议泵,所以可以將其放在一個state中占贫。
const [user_info, setUserInfo] = useState({name: 'susan', school: 'school-one'});
- useState的調(diào)用必須放在頂層調(diào)用,不能在循環(huán)先口、條件語句型奥、嵌套函數(shù)中。
//正確調(diào)用
export const Basic = () => {
const [count, setCount] = useState(0);
}
//錯誤調(diào)用
export const Basic = () => {
const flag = 1;
if(flag){
const [count, setCount] = useState(0);
}
}
必須放在頂層調(diào)用的原因是react對hook的存儲是是按順序的碉京,react會在第一次渲染時將state按useState的順序逐個放到全局的數(shù)組中厢汹,后邊每次更改state都會根據(jù)順序去取state。如果不在非頂層調(diào)用的話谐宙,就會導(dǎo)致根據(jù)初始的序號取不到對應(yīng)的state烫葬。
(2) useEffect:
寫法如下:useEffect(()=>{}, [])
- 第二個參數(shù)傳與不傳影響很大(第二個參數(shù)為依賴項,可選)
import React, { useState, useEffect } from 'react';
export const BasicInfo = () => {
const [count, setCount] = useState(0);
//不寫第二個參數(shù)凡蜻,
useEffect(()=>{
document.title = `You clicked ${count} times`;
})
//第二個參數(shù)為空
useEffect(()=>{
document.title = `You clicked ${count} times`;
}, [])
//第二個參數(shù)有值
useEffect(()=>{
document.title = `You clicked ${count} times`;
}, count)
}
以上三種寫法所導(dǎo)致的結(jié)果分別是:
- 不寫第二個參數(shù)時:useEffect中第一個函數(shù)參數(shù)在每次渲染后都會執(zhí)行一次搭综。
- 第二個參數(shù)為空時:函數(shù)只有在第一次渲染后會執(zhí)行,相當(dāng)于componentDidMount划栓。
- 第二個參數(shù)不為空時兑巾,函數(shù)只有在該參數(shù)發(fā)生變化時,才會執(zhí)行忠荞。(可取闪朱,避免了不必要的執(zhí)行)
注意:useEffect會在每次渲染后都會執(zhí)行,只是第一個回調(diào)函數(shù)會依據(jù)情況來執(zhí)行
- 依賴項是否發(fā)生了變化钻洒?
import React, { useState, useEffect } from 'react';
export const BasicInfo = () => {
const [count, setCount] = useState(0);
const [item_list, setItemList] = useState([]);
//第二個參數(shù)為基本數(shù)據(jù)類型(趁機回想一下基本數(shù)據(jù)類型都有什么來著奋姿?)
useEffect(()=>{
console.log('count...', count);
}, [count])
//第二個參數(shù)為為引用數(shù)據(jù)類型
useEffect(()=>{
console.log('item_liist...', item_list)
}, [item_list])
//第二個參數(shù)每次都是一個新的數(shù)組
useEffect(()=>{
console.log('item_liist...', item_list)
}, [...item_list])
}
- 當(dāng)?shù)诙€參數(shù)為基本數(shù)據(jù)類型時,那么上述寫法沒有任何問題素标;
- 當(dāng)?shù)诙€參數(shù)為引用數(shù)據(jù)類型時称诗,由于react比較依賴是否發(fā)生變化的方法是簡單的比較,所以當(dāng)還是同一個數(shù)組時头遭,它并不會繼續(xù)判斷數(shù)組中的內(nèi)容是否發(fā)生變化寓免,就會直接認(rèn)定該依賴沒有變化癣诱。
解決辦法就是將依賴項每次都換成一個新的數(shù)組,如第三種寫法袜香。
(3) useContext:
- 只是替代了consumer撕予,provider該怎么寫還是得怎么寫
// 創(chuàng)建 Context
const Context = React.createContext();//context包含consumer和provider
function Father() {
return (
//使用provider給子組件提供數(shù)據(jù)
<Context.Provider data={'我是父組件的數(shù)據(jù)'}>
<Son />
</Context.Provider>
);
}
// 使用 Consumer 從上下文中獲取數(shù)據(jù)
function Son() {
return (
<Context.Consumer>
{data => <div>{data}</div>}
</Context.Consumer>
);
}
//使用useContext從上下文中獲取數(shù)據(jù)
function Son() {
const data = useContext(Context);
return <div>{data}</div>;
}
好啦,暫時就以基礎(chǔ)hook作為本次分享的結(jié)尾啦蜈首,因為發(fā)現(xiàn)太長的文章可能看到一半就不太想看了实抡,或者沒有那么長連續(xù)的時間來仔細(xì)看。以上可能在有些地方理解的有出入欢策,歡迎大家來討論哦吆寨。下一篇將繼續(xù)分享額外的hook的一些用法和注意的點。
希望大家能持續(xù)關(guān)注哦踩寇,留一些個人信息方便大家找到我哦啄清。知乎 github
附上個人公眾號哦,希望大家前來騷擾(也會經(jīng)常寫一些隨筆來記錄生活俺孙,畢竟人生漫漫)
[圖片上傳失敗...(image-98a2a6-1585186388805)]