hook簡介
Hook 是 React 16.8 的新增特性。它可以讓你在不編寫 class 的情況下使用 state 以及其他的 React 特性。踐行函數(shù)式編程的方式,在函數(shù)組件中使用磨镶,獲得class編寫的特性。
useState和useReducer
useState
-
useState
給一個(gè)state賦初始值,并且返回一個(gè)數(shù)組,一個(gè)用來讀取state的值,一個(gè)用來設(shè)置state的值咕娄。 -
useState
聲明的state在發(fā)生變化的時(shí)候,會觸發(fā)組件的更新珊擂。 -
setState
更新state是異步更新策略圣勒,參數(shù)有兩種,一種要更新的值未玻,第二種是一個(gè)更新的函數(shù),返回要更新的值胡控。 -
setState
不會進(jìn)行合并更新扳剿,只會全量更新,所以參數(shù)或回調(diào)函數(shù)值應(yīng)該要更新的值昼激。
const [state, setState] = useState(10);
// 賦值修改
setState(20) // 此方法就是將state修改為20
// 函數(shù)修改
setState(current => {
// current 是當(dāng)前state的值
return current + 20; // 修改state的值為40
// return state + 20; // 這樣也是可以的
})
useReducer
useState
的替代方案庇绽。它接收一個(gè)形如 (state, action) => newState
的 reducer,并返回當(dāng)前的 state 以及與其配套的 dispatch
方法橙困。(如果你熟悉 Redux 的話瞧掺,就已經(jīng)知道它如何工作了。)
用法簡介
const [state, dispatch] = useReducer(reducer, initialState, init);
-
reducer
是一個(gè)函數(shù)凡傅,該函數(shù)接受兩個(gè)參數(shù)辟狈,一個(gè)是state
一個(gè)是action
, 是修改state值得函數(shù)。 -
initialState
初始值 -
init
一個(gè)函數(shù)夏跷,用于初始化時(shí)調(diào)用的函數(shù)哼转,返回的就是初始化的state
。 -
useReducer
返回一個(gè)狀態(tài)state
和dispatch
槽华,state是返回狀態(tài)的值壹蔓,dispatch是可以發(fā)布事件來更新state的函數(shù),修改state值得韓式就是reducer猫态。
指定初始值
function counter = () => {
// 聲明count佣蓉,返回一個(gè)count 和 dispatch
const [count, dispatch] = useReducer((state, action) => {
// state 就是當(dāng)前count的值
// action 就是dispatch函數(shù)的參數(shù),可以為任何類型的數(shù)據(jù)
if (action === 'add') {
return state + 1;
} else {
return state - 1;
}
}, 0) // 默認(rèn)初始值為0
return (
<div>
<h1>{count}</h1>
{* 調(diào)用dispatch來更新count *}
<button onClick={dispatch('sub')}> -1 </button>
<button onClick={dispatch('sub')}> +1 </button>
</div>
)
}
惰性初始化
如果state初始化時(shí)需要有重置的操作或者state
初始化的值十分復(fù)雜亲雪,都可以使用useReducer
的第三個(gè)參數(shù)init
const initState = 10;
// 重置時(shí)會走該函數(shù)
const init = initState => {
return initState;
}
function counter = () => {
// 聲明count勇凭,返回一個(gè)count 和 dispatch
const [count, dispatch] = useReducer((state, action) => {
// state 就是當(dāng)前count的值
// action 就是dispatch函數(shù)的參數(shù),可以為任何類型的數(shù)據(jù)
if (action === 'add') {
return state + 1;
} else if(action === 'sub') {
return state - 1;
} else if (action === 'reset') {
return init(initState);
}
}, initState, init)
return (
<div>
<h1>{count}</h1>
{* 調(diào)用dispatch來更新count *}
<button onClick={dispatch('sub')}> -1 </button>
<button onClick={dispatch('sub')}> +1 </button>
<button onClick={dispatch('reset')}> 重置 </button>
</div>
)
}
注意
- 當(dāng)
useReducer
返回的state和當(dāng)前的一樣時(shí)义辕,是不會重新渲染該組件的套像,因?yàn)閞eact使用Object.js
來比較state。 - 對于state有比較復(fù)雜的賦值操作時(shí)建議使用
useReducer
來聲明和處理终息。
useEffect 和 useLayoutEffect
useEffect
-
useEffect
可以執(zhí)行副作用的操作 -
useEffect
有兩個(gè)參數(shù)夺巩,一個(gè)回調(diào)函數(shù)贞让,一個(gè)數(shù)組類型變量(這里面的值必須是依賴項(xiàng),就是使用useState等聲明的變量柳譬,可以觸發(fā)組件更新的變量) -
useEffect
會在每次render的時(shí)候都執(zhí)行喳张,可以通過參數(shù)控制只在初始化時(shí)執(zhí)行、某些數(shù)據(jù)變化時(shí)執(zhí)行 - 相當(dāng)于class組件的聲明周期
componentDidMount
(組件渲染后執(zhí)行)和componentDidUpdate
(組件更新后執(zhí)行) -
useEffect
是一個(gè)回調(diào)函數(shù)美澳,如果返回一個(gè)函數(shù)時(shí)销部,則會在componentWillUnmount
(組件銷毀)時(shí)執(zhí)行
只在初始化時(shí)執(zhí)行
在第二個(gè)參數(shù)中傳遞[]
空數(shù)組,則只在初始化時(shí)調(diào)用
useEffect(() => {
// 初始化時(shí)獲取list數(shù)據(jù)
getList();
}, [])
在初始化和render更新時(shí)執(zhí)行
第二個(gè)參數(shù)不穿時(shí)制跟,會在初始化和更新時(shí)執(zhí)行
useEffect(() => {
getList();
})
在某些數(shù)據(jù)變化時(shí)執(zhí)行
// 在count數(shù)據(jù)變化的時(shí)候執(zhí)行
useEffect(() => {
getList();
}, [count]) // 數(shù)組中可以放許多的變量舅桩,在這些數(shù)據(jù)變化時(shí)都會執(zhí)行
useLayoutEffect
- 當(dāng)需要處理dom時(shí),使用
useEffect
會導(dǎo)致閃屏問題 -
useLayoutEffect
會在DOM更新完成后立即執(zhí)行雨膨,在瀏覽器進(jìn)行回執(zhí)之前運(yùn)行 - 回阻塞瀏覽器的繪制
useContext
介紹
- 接受一個(gè)context(React.createContext的返回值)對象
-
useContext
幫助我們跨越組件層級直接傳遞變量擂涛,實(shí)現(xiàn)共享,解決了組件之間傳值的問題聊记。 - context對象提供
provider
屬性將數(shù)據(jù)可以通過垮組件訪問撒妈,通過value將值傳給子組件 -
useContext
獲取的數(shù)據(jù)是跟隨數(shù)據(jù)源里的數(shù)據(jù)的變化而變化的。
const value = useContext(MyContext);
使用
// 父組件 parent.js
import React, { useState, createContext } from 'react'
import Children from './children.js'
const Parent = () => {
const [count, setCount] = useState(10);
const CountContext = createContext();
return (
<>
<CountContext.Provider value={{count}}>
<Children countContext={CountContext}/>
</CountContext.Provider>
<h1>{ count }</h1>
<button onClick={() => setCount(count + 1)}>
加1
</button>
</>
)
}
// 子組件 children.js
import React, { useContext } from 'react'
const Children = props => {
// 這里的countContext是從組件傳遞過來的
// 也可以把createCount單獨(dú)放到模塊里面進(jìn)行引用
// 引用模式應(yīng)該比props好排监,可以適應(yīng)組件嵌套的問題
const { countContext } = props;
// 這里的數(shù)據(jù)是隨著父組件中的count數(shù)據(jù)變化而變化的
const countData = useContext(countContext);
return (
<h1>{ countData.count }</h1>
)
}
useRef 與 useImperativeHandle
useRef
const refContainer = useRef(initialValue);
-
useRef
返回一個(gè)可變的ref對象狰右,其.current
屬性就是被初始化傳入的參數(shù)。 -
useRef
中的值發(fā)生變化不會觸發(fā)組件的更新 -
useRef
可以用來保存任何可變值舆床。 - 綁定到原生html上面可以獲取該標(biāo)簽的方法棋蚌,綁定到自定義組件上,則不要通過
useImperativeHandle
將部分方法和屬性暴露出來挨队。 -
useImperativeHandle
可以在使用ref的時(shí)候自定義暴露給父組件的實(shí)例值和方法附鸽,需要和forwardRef
一起使用
useImperativeHandle
useImperativeHandle(ref, createHandle, [deps])
- ref:定義 current 對象的 ref createHandle:一個(gè)函數(shù),返回值是一個(gè)對象瞒瘸,即這個(gè) ref 的 current對象
- [deps]:即依賴列表坷备,當(dāng)監(jiān)聽的依賴發(fā)生變,useImperativeHandle 才會重新將子組件的實(shí)例屬性輸出到父組件
- ref 的 current 屬性上,如果為空數(shù)組情臭,則不會重新輸出省撑。
實(shí)例
// 父組件 parent.js
import React, {useRef} from 'react'
import Children from './children'
const Parent = () => {
const childRef = useRef();
return (
<>
<Children ref={childRef} />
<button onClick={() => childRef.current.addCount()} >+1</button>
</>
)
}
// children.js
import React, {useState, useImperativeHandle, forwardRef} from 'react'
// 第二個(gè)參數(shù)才是ref,第一個(gè)是props
const Children = (props, ref) => {
const [count, setCount] = useState(1)
useImperativeHandle(ref, () => {
addCount: setCount
})
return (
<h1>{count}</h1>
)
}
// 需要使用forwardRef進(jìn)行一次轉(zhuǎn)發(fā)
export default forwardRef(Children);
useCallback和useMemo
- 都是用于數(shù)據(jù)緩存的方法
- 都可以根據(jù)依賴項(xiàng)進(jìn)行刷新
- 主要是用在不需要隨著組件更新而更新的情況時(shí)俯在,用于優(yōu)化部分復(fù)雜函數(shù)更新問題
-
useCallback
返回函數(shù)竟秫,并不調(diào)用他們 -
useMemo
調(diào)用函數(shù),返回執(zhí)行的結(jié)果
用法
// useCallback 返回一個(gè)函數(shù)
const memoizedCallback = useCallback(
() => {
doSomething(a, b);
},
[a, b],
);
// useMemo 執(zhí)行函數(shù)跷乐,返回執(zhí)行的結(jié)果
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
例子
// parent.js
import React, {useState, useCallback, useMemo} from 'react'
import ChildrenComponent from './Children.js'
const Parent = () => {
const [count, setCount] = useState(10)
const [num, setNum] = useState(10)
// 只有count變化的時(shí)候countName才會更新肥败,可以作為優(yōu)化部分
const countName = useMemo(() => `年齡:${count}`, [count]);
// 這樣,只有在num更新的時(shí)候,children組件才會更新
const setDataNum = useCallback(() => setNum(num + 10), [num]);
return (
<div>
<h1>
{countName}
</h1>
<button onClick={() => setCount(count + 1)}>+1</button>
<H1>NUM {num}</H1>
<ChildrenComponent setData={setDataNum}/>
</div>
)
}
// children.js
import React from 'react';
const Children = ({setData}) => {
return (
<button onClick={() => setData()}>加10</button>
)
}
export React.memo(Children)