hook簡(jiǎn)介
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í)候,會(huì)觸發(fā)組件的更新。 -
setState更新state是異步更新策略,參數(shù)有兩種,一種要更新的值,第二種是一個(gè)更新的函數(shù),返回要更新的值。 -
setState不會(huì)進(jìn)行合并更新,只會(huì)全量更新,所以參數(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)知道它如何工作了。)
用法簡(jiǎn)介
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í)會(huì)走該函數(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í),是不會(huì)重新渲染該組件的,因?yàn)閞eact使用Object.js來比較state。 - 對(duì)于state有比較復(fù)雜的賦值操作時(shí)建議使用
useReducer來聲明和處理。
useEffect 和 useLayoutEffect
useEffect
-
useEffect可以執(zhí)行副作用的操作 -
useEffect有兩個(gè)參數(shù),一個(gè)回調(diào)函數(shù),一個(gè)數(shù)組類型變量(這里面的值必須是依賴項(xiàng),就是使用useState等聲明的變量,可以觸發(fā)組件更新的變量) -
useEffect會(huì)在每次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í),則會(huì)在componentWillUnmount(組件銷毀)時(shí)執(zhí)行
只在初始化時(shí)執(zhí)行
在第二個(gè)參數(shù)中傳遞[]空數(shù)組,則只在初始化時(shí)調(diào)用
useEffect(() => {
// 初始化時(shí)獲取list數(shù)據(jù)
getList();
}, [])
在初始化和render更新時(shí)執(zhí)行
第二個(gè)參數(shù)不穿時(shí),會(huì)在初始化和更新時(shí)執(zhí)行
useEffect(() => {
getList();
})
在某些數(shù)據(jù)變化時(shí)執(zhí)行
// 在count數(shù)據(jù)變化的時(shí)候執(zhí)行
useEffect(() => {
getList();
}, [count]) // 數(shù)組中可以放許多的變量,在這些數(shù)據(jù)變化時(shí)都會(huì)執(zhí)行
useLayoutEffect
- 當(dāng)需要處理dom時(shí),使用
useEffect會(huì)導(dǎo)致閃屏問題 -
useLayoutEffect會(huì)在DOM更新完成后立即執(zhí)行,在瀏覽器進(jìn)行回執(zhí)之前運(yùn)行 - 回阻塞瀏覽器的繪制
useContext
介紹
- 接受一個(gè)context(React.createContext的返回值)對(duì)象
-
useContext幫助我們跨越組件層級(jí)直接傳遞變量,實(shí)現(xiàn)共享,解決了組件之間傳值的問題。 - context對(duì)象提供
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對(duì)象,其.current屬性就是被初始化傳入的參數(shù)。 -
useRef中的值發(fā)生變化不會(huì)觸發(fā)組件的更新 -
useRef可以用來保存任何可變值。 - 綁定到原生html上面可以獲取該標(biāo)簽的方法,綁定到自定義組件上,則不要通過
useImperativeHandle將部分方法和屬性暴露出來。 -
useImperativeHandle可以在使用ref的時(shí)候自定義暴露給父組件的實(shí)例值和方法,需要和forwardRef一起使用
useImperativeHandle
useImperativeHandle(ref, createHandle, [deps])
- ref:定義 current 對(duì)象的 ref createHandle:一個(gè)函數(shù),返回值是一個(gè)對(duì)象,即這個(gè) ref 的 current對(duì)象
- [deps]:即依賴列表,當(dāng)監(jiān)聽的依賴發(fā)生變,useImperativeHandle 才會(huì)重新將子組件的實(shí)例屬性輸出到父組件
- ref 的 current 屬性上,如果為空數(shù)組,則不會(huì)重新輸出。
實(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才會(huì)更新,可以作為優(yōu)化部分
const countName = useMemo(() => `年齡:${count}`, [count]);
// 這樣,只有在num更新的時(shí)候,children組件才會(huì)更新
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)