1.useState
用法:
// initialState也可以接受一個(gè)函數(shù)
const [state, setState] = useState(initialState)
注意事項(xiàng)1: 不可局部更新
- Q: 如果state是一個(gè)對(duì)象蜜自,能否更新部分state
A: 不行敬扛,因?yàn)閟etState不會(huì)幫我們合并屬性,解決方法是用擴(kuò)展運(yùn)算符將之前的state對(duì)象擴(kuò)展
注意事項(xiàng)2: 不可局部更新
- 如果state對(duì)象地址沒有變化蒙畴,那么react就認(rèn)為數(shù)據(jù)沒有變化
注意事項(xiàng)3: setState可以接受函數(shù)為參數(shù)
setState(i => i + 1)
優(yōu)先使用這種方式會(huì)避免一些bug酪耕,比如在一個(gè)函數(shù)點(diǎn)擊中執(zhí)行兩次setState(i + 1),此時(shí)最后的結(jié)果只會(huì)加一次滓彰,但是執(zhí)行兩次setState(i => i + 1)此時(shí)結(jié)果是正常的
2.useReducer
注意事項(xiàng)1: 用來踐行redux思想
- 分為四部:
- 創(chuàng)建初始值initialState
- 創(chuàng)建所有操作reducer(state, action)
- 傳給reducer控妻,得到讀和寫api
- 調(diào)用dispatch寫操作
總的來說useReducer是useState的復(fù)雜版
注意事項(xiàng)2: 如何代替redux
- 將數(shù)據(jù)集中在一個(gè)store中
- 將所有操作集中在reducer中
- 創(chuàng)造一個(gè)Context
- 創(chuàng)建對(duì)數(shù)據(jù)讀寫的api
- 將第4步的內(nèi)容放到第3部的Context
- 用Context.Provider將Context提供給所有組件
- 各個(gè)組件用useContext獲取讀寫api
3.useContext
- 上下文
- 全局變量是全局的上下文
- 上下文是局部的全局變量
- 使用方法
- 使用C = createContext(initialState)創(chuàng)建上下文
- 使用C.Provider圈定作用域
- 在作用域內(nèi)使用useContext(C)來使用上下文
4.useEffect
- 副作用
- 對(duì)環(huán)境的改變即是副作用,如修改document.title
- 但我們不一定非得把副作用放在useEffect
- 實(shí)際上每次都是在render之后執(zhí)行揭绑,可以理解為afterRender
- 用途
- 作為componentDidMount使用弓候,第二個(gè)參數(shù)使用[]
- 作為componentDidUpdate使用,可在數(shù)組中指定依賴
- 作為componentWillUnMout使用他匪,通過return 函數(shù)使用菇存,組件要卸載的時(shí)候執(zhí)行
- 如果同時(shí)存在多個(gè)useEffect,會(huì)按照次序執(zhí)行
5.useMemo
- 理解useMemo
- react默認(rèn)會(huì)有多余的render邦蜜,父組件更新撰筷,即使子組件的props沒變,子組件還會(huì)重新渲染畦徘,useMemo的作用是只有props數(shù)據(jù)變了毕籽,子組件才會(huì)重新渲染
- 包裹一個(gè)函數(shù)式子組件
- 它有一個(gè)bug,即是擁有函數(shù)props的時(shí)候井辆,即使函數(shù)props沒變关筒,它,每次都會(huì)執(zhí)行杯缺,因?yàn)閍pp每次重新渲染都會(huì)重新生成一個(gè)函數(shù)蒸播,兩次函數(shù)不同,所以子組件會(huì)重新渲染。
- useMemo還可以接受函數(shù)作為參數(shù)袍榆,這個(gè)方案可以解決上述的bug
useMemo(() => {
retuen () => {
//todo
} //要緩存的函數(shù)
}胀屿,[]) // 需要添加依賴
useCallback就是為了useMemo方便寫一點(diǎn)創(chuàng)造出來的,二者并沒有太大區(qū)別包雀。如下圖宿崭,此時(shí)不用返回函數(shù),只需要在useCallback寫邏輯即可才写。
useCallback(() => {
// todo
console.log(xxx)
}葡兑,[]) // 需要添加依賴
6.useRef
- 作用
在組件render時(shí)不變的值
用法:ref.current
至于為什么不能直接使用ref,非得使用ref.current赞草,因?yàn)闉榱吮WC多次useRef是同一個(gè)值讹堤,只有對(duì)象引用才辦得到
在這里可以稍微總結(jié)一下了。
- useState/useReducer 每次render的時(shí)候state都會(huì)變
- useMemo/useCallback 每次render的時(shí)候厨疙,依賴變了洲守,state就會(huì)變
- useRef ,每次render的時(shí)候都不會(huì)變
這里有個(gè)hack操作沾凄,我們可以把ref當(dāng)做state操作
const ref = useRef(0)
const click = () => {
ref.current += 1
console.log(ref.current)
}
點(diǎn)擊按鈕的時(shí)候視圖不會(huì)更新梗醇,但是我們打印ref.current會(huì)發(fā)現(xiàn)它變了,我們得想個(gè)辦法讓render執(zhí)行
const ref = useRef(0)
const [m, setM] = useState(0)
const click = () => {
ref.current += 1
setM(i => i + 1)
console.log(ref.current)
}
此時(shí)我們會(huì)發(fā)現(xiàn)視圖也更新了搭独,原因是執(zhí)行setM的時(shí)候會(huì)讓視圖更新婴削,雖然setM這個(gè)函數(shù)跟我們寫的邏輯豪不相關(guān)
7.forwardRef
函數(shù)式組件不能直接使用ref
const ref = useRef(null)
const app = (
<div>
<Button ref = {ref}/>
</div>
)
const Button = (props) => {
console.log(props)
return <button></button>
}
此時(shí)會(huì)報(bào)錯(cuò)廊镜,我們看打印信息會(huì)發(fā)現(xiàn)沒有ref屬性牙肝,這是因?yàn)閞eact做攔截了,若想使用ref嗤朴,就得使用了
const ref = useRef(null)
const app = (
<div>
<Button2 ref = {ref}/>
</div>
)
const Button = (props, ref) => {
console.log(props)
return <button ref={ref}></button>
}
const Button2 = React.forwardRef(Button)
此時(shí)就能正常使用并且看到ref的打印信息了
那么為什么react要對(duì)props ref作這個(gè)限制呢配椭,可能是大部分時(shí)候不需要用到ref吧
7.useImperativeHandle
這個(gè)名字太難懂了,其實(shí)可以理解成setRef雹姊,也就是最后返回對(duì)ref并且作一些封裝吧股缸,目前沒有用到過
8.自定義hook
封裝hook,一般返回讀接口和寫接口吱雏,但是我們可以自定義返回增刪改查敦姻,這也是hook可以邏輯富用的主要原因
9.Stale Closure
這涉及到hooks心智負(fù)擔(dān)的原理,若想了解可查看我的文章使用react hooks的時(shí)候要注意過時(shí)的閉包