日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
十分鐘教你手寫九個(gè)常用的自定義Hooks

前言

Hook 是 React 16.8 的新增特性。它可以讓你在不編寫 class 的情況下使用 state 以及其他的 React 特性。本文是一篇以實(shí)戰(zhàn)為主的文章,主要講解實(shí)際項(xiàng)目中如何使用hooks以及一些最佳實(shí)踐,不會(huì)一步步再介紹一遍react hooks的由來(lái)和基本使用,因?yàn)閷慼ooks的文章很多,而且官網(wǎng)對(duì)于react hooks的介紹也很詳細(xì),所以大家不熟悉的可以看一遍官網(wǎng)。

創(chuàng)新互聯(lián)公司于2013年成立,是專業(yè)互聯(lián)網(wǎng)技術(shù)服務(wù)公司,擁有項(xiàng)目做網(wǎng)站、成都網(wǎng)站建設(shè)網(wǎng)站策劃,項(xiàng)目實(shí)施與項(xiàng)目整合能力。我們以讓每一個(gè)夢(mèng)想脫穎而出為使命,1280元巴彥淖爾做網(wǎng)站,已為上家服務(wù),為巴彥淖爾各地企業(yè)和個(gè)人服務(wù),聯(lián)系電話:028-86922220

正文

1、 react hooks核心API使用注意事項(xiàng)

筆者在項(xiàng)目中常用的hooks主要有useState, useEffect,useCallback,useMemo,useRef。當(dāng)然像useReducer, useContext, createContext這些鉤子在H5游戲中也會(huì)使用,因?yàn)椴恍枰S護(hù)錯(cuò)綜復(fù)雜的狀態(tài),所以我們完全可以由上述三個(gè)api構(gòu)建一個(gè)自己的小型redux(后面會(huì)介紹如何實(shí)現(xiàn)小型的redux)來(lái)處理全局狀態(tài),但是對(duì)于企業(yè)復(fù)雜項(xiàng)目來(lái)說(shuō),我們使用redux及其生態(tài)會(huì)更加高效一些。

我們?cè)谑褂胔ooks和函數(shù)組件編寫我們的組件時(shí),第一個(gè)要考慮的就是渲染性能,我們知道如果在不做任何處理時(shí),我們?cè)诤瘮?shù)組件中使用setState都會(huì)導(dǎo)致組件內(nèi)部重新渲染,一個(gè)比較典型的場(chǎng)景:

當(dāng)我們?cè)谌萜鹘M件手動(dòng)更新了任何state時(shí),容器內(nèi)部的各個(gè)子組件都會(huì)重新渲染,為了避免這種情況出現(xiàn),我們一般都會(huì)使用memo將函數(shù)組件包裹,來(lái)達(dá)到class組件的pureComponent的效果:

import React, { memo, useState, useEffect } from 'react'
const A = (props) => {
console.log('A1')
useEffect(() => {
console.log('A2')
})
return
A

}
const B = memo((props) => {
console.log('B1')
useEffect(() => {
console.log('B2')
})
return
B

})
const Home = (props) => {
const [a, setA] = useState(0)
useEffect(() => {
console.log('start')
setA(1)
}, [])
return

}

當(dāng)我們將B用memo包裹后,狀態(tài)a的更新將不會(huì)導(dǎo)致B組件重新渲染。其實(shí)僅僅優(yōu)化這一點(diǎn)還遠(yuǎn)遠(yuǎn)不夠的,比如說(shuō)我們子組件用到了容器組件的某個(gè)變量或者函數(shù),那么當(dāng)容器內(nèi)部的state更新之后,這些變量和函數(shù)都會(huì)重新賦值,這樣就會(huì)導(dǎo)致即使子組件使用了memo包裹也還是會(huì)重新渲染,那么這個(gè)時(shí)候我們就需要使用useMemo和useCallback了。

useMemo可以幫我們將變量緩存起來(lái),useCallback可以緩存回調(diào)函數(shù),它們的第二個(gè)參數(shù)和useEffect一樣,是一個(gè)依賴項(xiàng)數(shù)組,通過配置依賴項(xiàng)數(shù)組來(lái)決定是否更新。

import React, { memo, useState, useEffect, useMemo } from 'react'
const Home = (props) => {
const [a, setA] = useState(0)
const [b, setB] = useState(0)
useEffect(() => {
setA(1)
}, [])
const add = useCallback(() => {
console.log('b', b)
}, [b])
const name = useMemo(() => {
return b + 'xuxi'
}, [b])
return

}

此時(shí)a更新后B組件不會(huì)再重新渲染。以上幾個(gè)優(yōu)化步驟主要是用來(lái)優(yōu)化組件的渲染性能,我們平時(shí)還會(huì)涉及到獲取組件dom和使用內(nèi)部閉包變量的情景,這個(gè)時(shí)候我們就可以使用useRef。

useRef返回一個(gè)可變的 ref 對(duì)象,其 .current 屬性被初始化為傳入的參數(shù)(initialValue)。返回的 ref 對(duì)象在組件的整個(gè)生命周期內(nèi)保持不變。

function AutoFocusIpt() {
const inputEl = useRef(null);
const useEffect(() => {
// `current` 指向已掛載到 DOM 上的文本輸入元素
inputEl.current.focus();
}, []);
return (
<>


);
}

除了以上應(yīng)用場(chǎng)景外,我們還可以利用它來(lái)實(shí)現(xiàn)class組件的setState的功能,具體實(shí)現(xiàn)后面會(huì)有介紹。

2、實(shí)現(xiàn)一個(gè)小型redux

實(shí)現(xiàn)redux我們會(huì)利用之前說(shuō)的useReducer, useContext, createContext這三個(gè)api,至于如何實(shí)現(xiàn)redux,其實(shí)網(wǎng)上也有很多實(shí)現(xiàn)方式,這里筆者寫一個(gè)demo供大家參考:

// actionType.js
const actionType = {
INSREMENT: 'INSREMENT',
DECREMENT: 'DECREMENT',
RESET: 'RESET'
}
export default actionType
// actions.js
import actionType from './actionType'
const add = (num) => ({
type: actionType.INSREMENT,
payload: num
})
const dec = (num) => ({
type: actionType.DECREMENT,
payload: num
})
const getList = (data) => ({
type: actionType.GETLIST,
payload: data
})
export {
add,
dec,
getList
}
// reducer.js
function init(initialCount) {
return {
count: initialCount,
total: 10,
user: {},
article: []
}
}
function reducer(state, action) {
switch (action.type) {
case actionType.INSREMENT:
return {count: state.count + action.payload};
case actionType.DECREMENT:
return {count: state.count - action.payload};
case actionType.RESET:
return init(action.payload);
default:
throw new Error();
}
}
export { init, reducer }

// redux.js
import React, { useReducer, useContext, createContext } from 'react'
import { init, reducer } from './reducer'

const Context = createContext()
const Provider = (props) => {
const [state, dispatch] = useReducer(reducer, props.initialState || 0, init);
return (

{ props.children }

)
}
export { Context, Provider }

其實(shí)還有更優(yōu)雅的方式實(shí)現(xiàn),筆者之前也寫了幾套redux模版,歡迎一起討論哈。接下來(lái)我們進(jìn)入正文,來(lái)帶大家實(shí)現(xiàn)幾個(gè)常用的自定義hooks。

3、實(shí)現(xiàn)自定義的useState,支持類似class組件setState方法

熟悉react的朋友都知道,我們使用class組件更新狀態(tài)時(shí),setState會(huì)支持兩個(gè)參數(shù),一個(gè)是更新后的state或者回調(diào)式更新的state,另一個(gè)參數(shù)是更新后的回調(diào)函數(shù),如下面的用法:

this.setState({num: 1}, () => {
console.log('updated')
})

但是hooks函數(shù)的useState第二個(gè)參數(shù)回調(diào)支持類似class組件的setState的第一個(gè)參數(shù)的用法,并不支持第二個(gè)參數(shù)回調(diào),但是很多業(yè)務(wù)場(chǎng)景中我們又希望hooks組件能支持更新后的回調(diào)這一方法,那該怎么辦呢?其實(shí)問題也很簡(jiǎn)單,我們只要對(duì)hooks原理和api非常清楚的話,就可以通過自定義hooks來(lái)實(shí)現(xiàn),這里我們借助上面提到的useRef和useEffect配合useState來(lái)實(shí)現(xiàn)這一功能。

注:react hooks的useState一定要放到函數(shù)組件的最頂層,不能寫在ifelse等條件語(yǔ)句當(dāng)中,來(lái)確保hooks的執(zhí)行順序一致,因?yàn)閡seState底層采用鏈表結(jié)構(gòu)實(shí)現(xiàn),有嚴(yán)格的順序之分。

我們先來(lái)看看實(shí)現(xiàn)的代碼:

import { useEffect, useRef, useState } from 'react'
const useXState = (initState) => {
const [state, setState] = useState(initState)
let isUpdate = useRef()
const setXState = (state, cb) => {
setState(prev => {
isUpdate.current = cb
return typeof state === 'function' ? state(prev) : state
})
}
useEffect(() => {
if(isUpdate.current) {
isUpdate.current()
}
})
return [state, setXState]
}
export default useXState

筆者利用useRef的特性來(lái)作為標(biāo)識(shí)區(qū)分是掛載還是更新,當(dāng)執(zhí)行setXstate時(shí),會(huì)傳入和setState一模一樣的參數(shù),并且將回調(diào)賦值給useRef的current屬性,這樣在更新完成時(shí),我們手動(dòng)調(diào)用current即可實(shí)現(xiàn)更新后的回調(diào)這一功能,是不是很巧妙呢?

4、實(shí)現(xiàn)自定義的useDebounce

節(jié)流函數(shù)和防抖函數(shù)想必大家也不陌生,為了讓我們?cè)陂_發(fā)中更優(yōu)雅的使用節(jié)流和防抖函數(shù),我們往往需要讓某個(gè)state也具有節(jié)流防抖的功能,或者某個(gè)函數(shù)的調(diào)用,為了避免頻繁調(diào)用,我們往往也會(huì)采取節(jié)截流防抖這一思想,原生的節(jié)流防抖函數(shù)可能如一下代碼所示:

// 節(jié)流
function throttle(func, ms) {
let previous = 0;
return function() {
let now = Date.now();
let context = this;
let args = arguments;
if (now - previous > ms) {
func.apply(context, args);
previous = now;
}
}
}
// 防抖
function debounce(func, ms) {
let timeout;
return function () {
let context = this;
let args = arguments;
if (timeout) clearTimeout(timeout);
timeout = setTimeout(() => {
func.apply(context, args)
}, ms);
}
}

那么我們首先來(lái)實(shí)現(xiàn)一下防抖的hooks,代碼如下:

import { useEffect, useRef } from 'react'
const useDebounce = (fn, ms = 30, deps = []) => {
let timeout = useRef()
useEffect(() => {
if (timeout.current) clearTimeout(timeout.current)
timeout.current = setTimeout(() => {
fn()
}, ms)
}, deps)
const cancel = () => {
clearTimeout(timeout.current)
timeout = null
}
return [cancel]
}
export default useDebounce

由代碼可以知道,useDebounce接受三個(gè)參數(shù),分別為回調(diào)函數(shù),時(shí)間間隔以及依賴項(xiàng)數(shù)組,它暴露了cancel API,主要是用來(lái)控制何時(shí)停止防抖函數(shù)用的。具體使用如下:

// ...
import { useDebounce } from 'hooks'
const Home = (props) => {
const [a, setA] = useState(0)
const [b, setB] = useState(0)
const [cancel] = useDebounce(() => {
setB(a)
}, 2000, [a])
const changeIpt = (e) => {
setA(e.target.value)
}
return


{ b } { a }

}

以上代碼就實(shí)現(xiàn)了state的debounce的功能,具體效果如下圖所示:

5、 實(shí)現(xiàn)自定義的useThrottle

同理,我們繼續(xù)來(lái)實(shí)現(xiàn)節(jié)流的hooks函數(shù)。直接上代碼:

import { useEffect, useRef, useState } from 'react'
const useThrottle = (fn, ms = 30, deps = []) => {
let previous = useRef(0)
let [time, setTime] = useState(ms)
useEffect(() => {
let now = Date.now();
if (now - previous.current > time) {
fn();
previous.current = now;
}
}, deps)
const cancel = () => {
setTime(0)
}
return [cancel]
}
export default useThrottle

代碼和自定義useDebounce類似,但需要注意一點(diǎn)就是為了實(shí)現(xiàn)cancel功能,我們使用了內(nèi)部state來(lái)處理,通過控制時(shí)間間隔來(lái)取消節(jié)流效果,當(dāng)然還有很多其他方法可以實(shí)現(xiàn)這個(gè)hooks API。具體效果如下:

6、實(shí)現(xiàn)自定義useTitle

自定義的useTitle hooks其實(shí)使用場(chǎng)景也很多,因?yàn)槲覀兡壳按蟛糠猪?xiàng)目都是采用SPA或者混合SPA的方式開發(fā),對(duì)于不同的路由我們同樣希望想多頁(yè)應(yīng)用一樣能切換到對(duì)應(yīng)的標(biāo)題,這樣可以讓用戶更好的知道頁(yè)面的主題和內(nèi)容。這個(gè)hooks的實(shí)現(xiàn)也很簡(jiǎn)單,我們直接上代碼:

import { useEffect } from 'react'
const useTitle = (title) => {
useEffect(() => {
document.title = title
}, [])
return
}
export default useTitle

以上代碼可以看出我們只需要在useEffect中設(shè)置document的title屬性就好了,我們不需要return任何值。其實(shí)還有更優(yōu)雅和復(fù)雜的實(shí)現(xiàn)方法,這里就不一一舉例了。具體使用如下:

const Home = () => {
// ...
useTitle('趣談前端')
return
home

}

7、實(shí)現(xiàn)自定義的useUpdate

我們都知道如果想讓組件重新渲染,我們不得不更新state,但是有時(shí)候業(yè)務(wù)需要的state是沒必要更新的,我們不能僅僅為了讓組件會(huì)重新渲染而強(qiáng)制讓一個(gè)state做無(wú)意義的更新,所以這個(gè)時(shí)候我們就可以自定義一個(gè)更新的hooks來(lái)優(yōu)雅的實(shí)現(xiàn)組件的強(qiáng)制更新,實(shí)現(xiàn)代碼如下:

import { useState } from 'react'
const useUpdate = () => {
const [, setFlag] = useState()
const update = () => {
setFlag(Date.now())
}
return update
}
export default useUpdate

以上代碼可以發(fā)現(xiàn),我們useUpdate鉤子返回了一個(gè)函數(shù),該函數(shù)就是用來(lái)強(qiáng)制更新用的。使用方法如下:

const Home = (props) => {
// ...
const update = useUpdate()
return

{Date.now()}


}

效果如下:

8、實(shí)現(xiàn)自定義的useScroll

自定義的useScroll也是高頻出現(xiàn)的問題之一,我們往往會(huì)監(jiān)聽一個(gè)元素滾動(dòng)位置的變化來(lái)決定展現(xiàn)那些內(nèi)容,這個(gè)應(yīng)用場(chǎng)景在H5游戲開發(fā)中應(yīng)用十分廣泛,接下來(lái)我們來(lái)看看實(shí)現(xiàn)代碼:

import { useState, useEffect } from 'react'
const useScroll = (scrollRef) => {
const [pos, setPos] = useState([0,0])
useEffect(() => {
function handleScroll(e){
setPos([scrollRef.current.scrollLeft, scrollRef.current.scrollTop])
}
scrollRef.current.addEventListener('scroll', handleScroll, false)
return () => {
scrollRef.current.removeEventListener('scroll', handleScroll, false)
}
}, [])
return pos
}
export default useScroll

由以上代碼可知,我們?cè)阢^子函數(shù)里需要傳入一個(gè)元素的引用,這個(gè)我們可以在函數(shù)組件中采用ref和useRef來(lái)獲取到,鉤子返回了滾動(dòng)的x,y值,即滾動(dòng)的左位移和頂部位移,具體使用如下:

import React, { useRef } from 'react'
import { useScroll } from 'hooks'
const Home = (props) => {
const scrollRef = useRef(null)
const [x, y] = useScroll(scrollRef)
return




{ x }, { y }


}

通過使用useScroll,鉤子將會(huì)幫我們自動(dòng)監(jiān)聽容器滾動(dòng)條的變化從而實(shí)時(shí)獲取滾動(dòng)的位置。具體效果如下:

9、 實(shí)現(xiàn)自定義的useMouse和實(shí)現(xiàn)自定義的createBreakpoint

自定義的useMouse和createBreakpoint的實(shí)現(xiàn)方法和useScroll類似,都是監(jiān)聽窗口或者dom的事件來(lái)自動(dòng)更新我們需要的值,這里我就不一一實(shí)現(xiàn)了,如果不懂的可以和我交流。通過這些自定義鉤子能大大提高我們代碼的開發(fā)效率,并將重復(fù)代碼進(jìn)行有效復(fù)用,所以大家在工作中可以多嘗試。

當(dāng)我們寫了很多自定鉤子時(shí),一個(gè)好的開發(fā)經(jīng)驗(yàn)就是統(tǒng)一管理和分發(fā)這些鉤子,筆者建議可以在項(xiàng)目中單獨(dú)建一個(gè)hooks的目錄專門存放這些可復(fù)用的鉤子,方便管理和維護(hù)。如下:


本文標(biāo)題:十分鐘教你手寫九個(gè)常用的自定義Hooks
分享URL:
http://m.5511xx.com/article/cdpidhj.html