Hook 是能讓你在函數組件中“鉤入” React 特性的函數(hook是react16.8的新增特性,讓函數組件可以使用state以及其他react特性)
一、useState
useState用來創建state變量
const [state,setState]=React.useState(0)
const [state,setState]=React.useState({name:'frank',age:'18})
// 0或{name:'frank'}為state的初始值,setState函數可以改變state的值
setState(2)
setState({...state,name:'newName'})
// 由于react更新是重新生成一個新對象,react只會生成傳入的對象,所以如果不想另一個屬性遺失就必須使用將原本的對象展開放上去
二、useReducer
useReducer是useState的綜合版
useReducer接受兩個參數,一個操作函數,一個初始值
const [state,dispatch]=React.useReducer(fn,obj)
// 調用
dispatch({type:'name'}) //參數傳給fn的action
函數
const fn =(state,action)=>{
// state為原始state;action為傳過來的參數
return 新state(obj)的值
}
初始值
const obj={
name:'frank',
age:'18'
}
三、useContext
useContext上下文,上下文是局部(context.provider包裹)的全局變量
// 創建上下文
const Context = React.createContext(null)
// 使用context.provider圈定作用域
<Context.Provider value={{ n, setN }}>
app
<Father />
</Context.Provider>
// 使用context傳過來的值
const Father = () => {
const { n, setN } = useContext(Context) // 使用上下文的值
const onButton = () => {
setN(n + 1)
}
return (
<div>
father:{n}
<button onClick={onButton}>+1</button>
</div>
)
}
注意:這個地方更改n的值界面也隨之更改,并不是因為react的響應式,而是因為每次調用setN都會重新渲染App,發現n的值變了界面才變
四、useEffect
useEffect 使用副作用,名字取得不是很好理解,平替的名字可以是afterRender(渲染之后執行),
// 以下三個都會在第一次渲染執行一次
useEffect(() => {}) //任何元素變化都執行
useEffect(() => {},[n]) //n變化執行
useEffect(() => {},[]) //掛載執行
useEffect(() => {
return ()=>{} //消亡執行
},[])
通常情況下useEffect都是由上往下執行,但是useLayoutEffect在它之前執行
useLayoutEffect
useLayoutEffect是在Dom創建之后,渲染之前執行。原理如下:
useLayoutEffect.png
五、memo、useMemo、和useCallback
這三個api都是為了防多次渲染而來的
舉個栗子,以下代碼中m的值沒有變,但是Child還是執行了。。怎么防止這種情況呢?
const App = () => {
const [n, setN] = useState(0)
const [m, setM] = useState('a')
return (
<div>
<div>grandfather</div>
<button onClick={() => { setN(i => i + 1) }}>+1</button>
<Father n={n} />
<Child m={m} />
</div>
)
}
const Father = (props) => {
console.log('father執行了')
return (
<div>father:{props.n}</div>
)
}
const Child = (props) => {
console.log('child執行了')
return (
<div>child:{props.m}</div>
)
}
- 將組件傳給React.memo
const Child2 = React.memo(Child) //將Child2代替Child使用
// 或
const Child = memo(
() => {
return (
<div>child:{props.m}</div>
)
}
)
只是這么做有一個bug,那就是當props傳遞的是函數時,這個方法不生效。這時就要用到useMemo了
// 假如以下是傳遞的函數
const fn=React.useMemo(()=>{return ()=>{}},[m]) //當n變化時被傳的組件才會執行
只是這么寫是不是看著有點怪怪的。于是react提供了個語法糖,可以直接傳個函數React.useCallback(fn,value)
const fn1 = React.useCallback(()=>{},[m])
其實這就有點類似于vue的computed了,有緩存當依賴的值變化才執行
六、useRef
useRef聲明一個不變的值(這里說的不變是指內存地址不變)
// 聲明
const count = useRef(0)
// 使用
count.current += 1
因為更改useRef的值不會導致app渲染,會造成結果變了UI沒更新。解決:
const [n, setn] = useState(0)
// 調用一下
setn()
使用useState的特性重新(手動)render一下App