關注公衆號,更多精彩內容等着你
前端
React Hook 指南
什麼是 Hook ?
❝Hook 是 React 16.8 的新增特性。它可讓你在不編寫 class 的狀況下使用 state 以及其餘的 React 特性。react
Hook
本質上就是一個函數,它簡潔了組件,有本身的狀態管理,生命週期管理,狀態共享。web❞
useState
useEffect
useContext
useReducer
Hook 出現解決了什麼 ?
❝❞
[ ] 組件之間狀態複用, 例如:使用useContext 能夠很好的解決狀態複用問題,或者自定義 Hook
來定製符合本身業務場景遇到的狀態管理。[ ] 在函數組件中 生命週期的使用,更好的設計封裝組件。在函數組件中是不能直接使用生命週期的,經過 Hook
很好的解決了此問題。[ ] 函數組件與 class 組件的差別,還要區分兩種組件的使用場景。使用 Hook
徹底不用去想這些,它可使用更多React
新特性。
何時使用 Hook ?
❝❞
在函數組件頂層調用
在 函數中使用 / 自定義
Hook
中使用
React
內置的 Hook
❝❞
useState
狀態管理
useEffect
生命週期管理
useContext
共享狀態數據
useMemo
緩存值
useRef
獲取Dom 操做
useCallback
緩存函數
useReducer
redux 類似
useImperativeHandle
子組件暴露值/方法
useLayoutEffect
完成反作用操做,會阻塞瀏覽器繪製
useState
狀態管理
❝在
class
組件中,咱們獲取state
是 經過this.state
來獲取的。面試而在
❞函數組件
中, 是沒有this
的, 咱們可使用Hook
提供的useState
來管理和維護state
.redux
useState
定義 / 使用
❝
const [state, setState] = useState(initialState)
數組❞
setState 爲更新 satate 方法
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 個生命週期函數:性能優化❞
componentDidmount
componentDidUpdate
componentWillUnmount
無需清除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對象在組件的整個生命週期內保持不變」。做用:
❞
獲取Dom操做,例如 獲取
input
焦點獲取子組件的實例(只有類組件可用)
在函數組件中的一個全局變量,不會由於重複 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
Redux
.....
本小節經過
❞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
的參數:❞
callback
是一個函數用於處理邏輯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,()=>{},[])
❞
參數1:子組件向父組件暴露的實例
參數2:函數,傳遞的父組件可操做的實例和方法
參數3:監聽狀態,更新狀態
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>
)
}
結語
關注公衆號 「前端自學社區」,便可獲取更多前端高質量文章!
關注後回覆關鍵詞「加羣」, 便可加入 「前端自學交流羣」,共同窗習進步。
關注後添加我微信拉你進技術交流羣
本文分享自微信公衆號 - 前端自學社區(gh_ce69e7dba7b5)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。