Hook 是 React 16.8 的新增特性。它可以讓你在不編寫 class 的情況下使用 state 以及其他的 React 特性。
Hook 本質上就是一個函數,它簡潔了組件,有自己的狀態管理,生命周期管理,狀態共享。
❞Hook 出現解決了什麼 ?❝[ ] 組件之間狀態復用, 例如:使用useContext 可以很好的解決狀態復用問題,或者自定義Hook 來定製符合自己業務場景遇到的狀態管理。[ ] 在函數組件中 生命周期的使用,更好的設計封裝組件。在函數組件中是不能直接使用生命周期的,通過 Hook 很好的解決了此問題。[ ] 函數組件與 class 組件的差異,還要區分兩種組件的使用場景。使用 Hook 完全不用去想這些,它可以使用更多 React 新特性。❞什麼時候使用 Hook ?❝❞React 內置的 Hook❝useImperativeHandle 子組件暴露值/方法useLayoutEffect完成副作用操作,會阻塞瀏覽器繪製❞useState 狀態管理❝在 class 組件中,我們獲取 state 是 通過 this.state 來獲取的。
而在函數組件中, 是沒有 this 的, 我們可以使用 Hook 提供的 useState 來管理和維護 state .
❞useState 定義 / 使用❝const [state, setState] = useState(initialState)
useState(initialState) initialState 為初始值❞完整慄子import {useState} from 'react';
export default () => {
const [data, setData] = useState('微信公眾號: 前端自學社區')
return (
<div>
<h1>{data}</h1>
{/* 更新 state */}
<button onClick={()=>{setData('微信公眾號: 前端自學社區 666')}}></button>
</div>
)
}
useEffect 生命周期管理定義❝useEffect 可以看作是 函數式 組件 的 生命周期管理。
因為在 函數式組件中無法直接使用生命周期,就必須託管 Hook 來進行管理使用了。
useEffect 可以使用的 3 個生命周期函數:
❞無需清除Effect 使用❝什麼是無需清除 Effect 使用?❝「React 更新 DOM 之後運行一些額外的代碼」
那麼它就是在生命周期的compoentDidmount 和 componentUpdate 中執行即可。
❞❞useEffect(() => {
//默認會執行
// 這塊相當於 class 組件 生命周期的
//compoentDidmount compoentDidUpdate
}, [])
清除Effect 使用❝1. 什麼是 清除Effect ?❝當組件進行卸載時,需要執行某些事件處理時,就需要用到 class 組件生命周期的 componentUnmount .
在 useEffect 中很方便使用,在內部返回一個方法即可,在方法中寫相應業務邏輯
❞2. 為什麼 要在 Effect 中返回一個函數 ?❝這是 effect 可選的清除機制。每個 effect 都可以返回一個清除函數。如此可以將添加和移除訂閱的邏輯放在一起。它們都屬於 effect 的一部分。
❞❞useEffect(()=>{
return () => {
console.log('組件卸載時執行')
}
})
監聽 state 變化❝可以通過控制 監聽 state 變化來實現相應的業務邏輯。
❞useEffect(() => {
// 監聽num,count 狀態變化
// 不監聽時為空 [] , 或者不寫
}, [num, count])
完整慄子import { useState, useEffect } from 'react';
export default () => {
const [num, setNum] = useState(0)
const [count, setCount] = useState(1)
useEffect(() => {
//默認會執行
// 這塊相當於 class 組件 生命周期的 compoentDidmount compoentDidUpdate
console.log(`num: ${num}`)
console.log(`count: ${count}`)
// 組件在卸載時,將會執行 return 中內容
return () => {
// 相當於 class 組件生命周期的 componentWillUnMount
console.log('測試')
}
}, [num])
return (
<div>
<h1>{num}</h1>
<button onClick={() => { setNum(num + 1) }}> 更新Num</button>
<hr />
<h1>{count}</h1>
<button onClick={() => { setCount(count + 1) }}> 更新Count</button>
</div>
)
}
useRef什麼是 useRef ?❝useRef 返回的是一個可變的ref對象,它的屬性current被初始化為傳入的參數(initialValue),「返回的ref對象在組件的整個生命周期內保持不變」。
作用:在函數組件中的一個全局變量,不會因為重複 render 重複申明❞慄子import {useRef} from 'react';
export default () => {
const inputRef = useRef({value:0})
return (
<div>
<h1>測試</h1>
<input type="text" ref={inputRef} />
<button onClick={()=>{console.log(inputRef.current.value)}}>獲取input 值</button>
<button onClick={()=>{inputRef.current.focus()}}>獲取input 焦點</button>
</div>
)
}
useContext 狀態數據共享Context 解決了什麼❝在日常開發中,我們父子組件都是通過props 來進行通信,如果遇到跨級組件通信 那麼我們就不好通過 props 來處理了。
這時候可以想想怎麼可以把 組件 狀態 共享出去使用?本小節通過 Context 來 達到組件數據共享❞什麼是 Context❝數據共享,任何組件都可訪問Context 數據。
在React 中,組件數據通過 prop 來達到 自上而下的傳遞數據,要想實現全局傳遞數據,那麼可以使用 Context .
注意:Context 主要應用場景在於很多不同層級的組件需要訪問同樣一些的數據。請謹慎使用,因為這會使得組件的復用性變差。
❞創建 Context❝在使用Context 前提,必須創建它,可以為它單獨創建一個文件來管理Context,
❞import React from 'react';
export const MyContext = React.createContext();
使用 Context❝在使用Context 時,它通常用在頂級組件(父組件上),它包裹的內部組件都可以享受到state 的使用和修改。
通過Context.Provider 來進行包裹,值通過value = {} 傳遞。
子組件如何使用 Context 傳遞過來的值 ?通過 useContext() Hook 可以很方便的拿到對應的值.❞// Context.js
import React from 'react';
export const MyContext = React.createContext();import { useContext } from 'react';
import {MyContext} from '../Context/index'
const result = {
code:200,
title:'添加數據成功'
}
const Son = () => {
const res = useContext(MyContext)
return (
<>
<div>
<h1>{res.code}</h1>
<hr/>
<h2>{res.title}</h2>
</div>
</>
)
}
export default () => {
return (
<MyContext.Provider value={result}>
<div>
<h1>前端自學社區</h1>
<Son/>
</div>
</MyContext.Provider>
)
}
useMemo 提升性能優化定義❝useMemo用於性能優化,通過記憶值來避免在每個渲染上執⾏高開銷的計算。
useMemo 參數:
useMemo 返回值是 memoized 值,具有緩存作用array控制useMemo重新執⾏的數組,array 中 的 state 改變時才會 重新執行useMemo注意:依賴對應的值,當對應的值發生變化時,才會重新計算(可以依賴另外一個 useMemo 返回的值)❞慄子
import { useState, useMemo} from 'react';
export default () => {
const [count, setCount] = useState(0)
const [num, setNum] = useState(0)
const newValue = useMemo(()=>{
console.log(`count 值為${count}`)
console.log(`num 值為 ${num}`)
return count+num
},[count])
return(
<div>
<h1>{count}</h1>
<button onClick={()=>{setCount(count+1)}}>count + 1</button>
<hr/>
<h1>{num}</h1>
<button onClick={()=>{setNum(num+1)}}>Num + 1</button>
<hr/>
<h2>{newValue}</h2>
</div>
)
}
解析慄子❝當點擊了 5 次更新 num 值,頁面中 newValue 的值始終顯示為 0,這是為什麼呢?
因為我在 useMemo 監聽記錄的是 count 的值,當 count 值發生變化時,頁面上的newValue 在會重新計算,雖然你點擊了 5 次 更新 num ,頁面沒有更新,但是已經緩存起來了,當點擊 更新 count 時,它會 計算 count+1 的值 和 num 緩存的值 , 最終結果 為 5。
減少了計算消耗。
❞useCallback 提升性能優化定義❝useCallback 可以說是 useMemo 的語法糖,能用 useCallback 實現的,都可以使用 useMemo, 常用於react的性能優化。
useCallback 的參數:
array 控制useCallback重新執⾏的數組,array改變時才會重新執⾏useCallback❞使用❝它的使用和useMemo 是一樣的,只是useCallback 返回的函數。
❞import { useState, useCallback} from 'react';
export default () => {
const [count, setCount] = useState(0)
const [num, setNum] = useState(0)
const newValue = useCallback(()=>{
console.log(`count 值為${count}`)
console.log(`num 值為 ${num}`)
return count+num
},[count])
return(
<div>
<h1>{count}</h1>
<button onClick={()=>{setCount(count+1)}}>count + 1</button>
<hr/>
<h1>{num}</h1>
<button onClick={()=>{setNum(num+1)}}>Num + 1</button>
<hr/>
{/* 調用useCallback 返回的值 */}
<h2>{newValue()}</h2>
</div>
)
}
小結❝useMemo 和 useCallback 功能類似,都是提升性能優化。
該採用哪種方式來最佳實踐,還有待探索。
歡迎 讀者 與 我交流。
網上對 useMemo 和 useCallback 的看法 ?❝useCallback 如果在函數式組件中的話,確實應該當作最佳實踐來用,但是使用它的目的除了要緩存依賴未改變的回調函數之外(與 useMemo 類似),還有一點是為了能夠在依賴發生變更時,能夠確保回調函數始終是最新的實例,從而不會引發一些意料之外的問題,我感覺後者才是使用 useCallback 的出發點,而非緩存。因為你想啊,即使不用 useCallback,假設這個回調函數也沒有任何依賴狀態,我直接把這個函數聲明在組件外部不也可以嗎?我直接使用 ref 不是更自由嗎?
useMemo 本身名字就是和緩存有關聯的,本質上就為了解決一個事情,在 render 裡面不要直接創建對象或者方法什麼的,因為組件每渲染一次,就會創建一次(比如 style 或者一些常量狀態),造成不必要的資源浪費。理想情況應當是,如果存在依賴,只在依賴變化時重新創建,不存在依賴,那就只創建一次。表面上看,如果所有狀態都用 useMemo,肯定沒什麼問題,但你還需從緩存的代價上來分析這個問題,如果使用 useMemo 緩存一個狀態的代價大於它帶來的優勢,那是不是反而適得其反了?
❞大家對 useMemo 和 useCallback 有何看法,歡迎在下方評論或者加我討論。❞useImperativeHandle定義❝useImperativeHandle 可以讓你在使用 ref 時自定義暴露給父組件的實例值。在大多數情況下,應當避免使用 ref 這樣的命令式代碼。useImperativeHandle 應當與 forwardRef 一起使用。
useImperativeHandle作用 :
❝子組件可以暴露給父組件 實例使用
❞格式: useImperativeHandle(ref,()=>{},[])
❞
import {useState,useImperativeHandle, forwardRef,useRef} from 'react';
const Son = forwardRef( (props,ref) => {
const inputRef = useRef(0)
const domRef = useRef()
const [state, setState] = useState('等待')
useImperativeHandle(ref,()=>({
focus:() => {inputRef.current.focus()},
domRef
}))
return (
<div>
<h1>{state}</h1>
<hr/>
<input type="text" ref={inputRef}/>
<h2 ref={domRef}>測試----useImperativeHandle</h2>
</div>
)
})
export default () => {
const refFather = useRef(0)
return (
<div>
<h1>父組件</h1>
<Son ref={refFather} />
<button onClick={()=>{refFather.current.focus()}}>獲取子組件實例-獲取input焦點</button>
<button onClick={()=>{console.log(refFather.current.domRef.current.innerHTML)}}>獲取子組件實例-獲取h2 Dom</button>
</div>
)
}
useReducer定義❝它是 useState 的替代方案。它接收一個形如 (state, action) => newState 的 reducer,並返回當前的 state以及與其配套的 dispatch 方法。
如果熟悉Redux 使用的話,用useReducer 就是輕車熟路了,發車了。
❞使用Reducer實現一個加減器
import {useReducer} from 'react';
export default () => {
const [state, dispatch] = useReducer((state,action)=> {
switch (action.type){
case 'addNum':
return {
num:state.num+1
}
case 'subtractNum':
return {
num:state.num-1
}
}
},{
num:0
})
return (
<div>
<h2>{state.num}</h2>
<button onClick={()=>{dispatch({type:'addNum'})}}> 增加num</button>
<button onClick={()=>{dispatch({type:'subtractNum'})}}> 增加num</button>
</div>
)
}