摘要:使用完成副作用操作,賦值給的函數(shù)會在組件渲染到屏幕之后。如此很容易產(chǎn)生,并且導(dǎo)致邏輯不一致。同時,這也是很多人將與狀態(tài)管理庫結(jié)合使用的原因之一。當(dāng)我們通過的第二個數(shù)組類型參數(shù),指明當(dāng)前的依賴,就能避免不相關(guān)的執(zhí)行開銷了。
前言
本文內(nèi)容大部分參考了 overreacted.io 博客一文,同時結(jié)合 React Hook 官方 文章,整理并歸納一些筆記和輸出個人的一些理解什么是 Hook ?
官方介紹:Hook 是 React 16.8 的新增特性。它可以讓你在不編寫 class 的情況下使用 state 以及其他的 React 特性。React 中內(nèi)置的 Hook API
基礎(chǔ) Hook
useState
// 傳入初始值,作為 state const [state, setState] = useState(initialState) // `惰性初始 state`;傳入函數(shù),由函數(shù)計算出的值作為 state // 此函數(shù)只在初始渲染時被調(diào)用 const [state, setState] = useState(() => { const initialState = someExpensiveComputation(props) return initialState })
useEffect
該 Hook 接收一個包含命令式、且可能有副作用代碼的函數(shù).
在函數(shù)組件主體內(nèi)(這里指在 React 渲染階段)改變 DOM、添加訂閱、設(shè)置定時器、記錄日志以及執(zhí)行其他包含副作用的操作都是不被允許的,因為這可能會產(chǎn)生莫名其妙的 bug 并破壞 UI 的一致性。
使用 useEffect 完成副作用操作,賦值給 useEffect 的函數(shù)會在組件渲染到屏幕之后。你可以把 effect 看作從 React 的純函數(shù)式世界通往命令式世界的逃生通道。
默認情況下,effect 將在每輪渲染結(jié)束后執(zhí)行,但你可以選擇讓它 在只有某些值改變的時候才執(zhí)行。詳情見后面。
清除 effect
通常,組件卸載時需要清除 effect 創(chuàng)建的諸如訂閱或計時器 ID 等資源。要實現(xiàn)這一點,useEffect 函數(shù)需返回一個清除函數(shù)。以下就是一個創(chuàng)建訂閱的例子:
useEffect(() => { const subscription = props.source.subscribe() return () => { // 清除訂閱 subscription.unsubscribe() } }, [依賴])
useContext
額外的 Hook
useReducer
useCallback
useMemo
useRef
useImperativeHandle
useLayoutEffect
useDebugValue
我們?yōu)槭裁催x擇使用 Hook ? 1. 在組件之間復(fù)用狀態(tài)邏輯很難React 沒有提供將可復(fù)用性行為“附加”到組件的途徑(例如,把組件連接到 store)。如果你使用過 React 一段時間,你也許會熟悉一些解決此類問題的方案,比如 render props 和 高階組件。但是這類方案需要重新組織你的組件結(jié)構(gòu),這可能會很麻煩,使你的代碼難以理解。如果你在 React DevTools 中觀察過 React 應(yīng)用,你會發(fā)現(xiàn)由 providers,consumers,高階組件,render props 等其他抽象層組成的組件會形成“嵌套地獄”。盡管我們可以在 DevTools 過濾掉它們,但這說明了一個更深層次的問題:React 需要為共享狀態(tài)邏輯提供更好的原生途徑。
你可以使用 Hook 從組件中提取狀態(tài)邏輯,使得這些邏輯可以多帶帶測試并復(fù)用。Hook 使你在無需修改組件結(jié)構(gòu)的情況下復(fù)用狀態(tài)邏輯。 這使得在組件間或社區(qū)內(nèi)共享 Hook 變得更便捷。
2. 復(fù)雜組件變得難以理解我們經(jīng)常維護一些組件,組件起初很簡單,但是逐漸會被狀態(tài)邏輯和副作用充斥。每個生命周期常常包含一些不相關(guān)的邏輯。例如,組件常常在 componentDidMount 和 componentDidUpdate 中獲取數(shù)據(jù)。但是,同一個 componentDidMount 中可能也包含很多其它的邏輯,如設(shè)置事件監(jiān)聽,而之后需在 componentWillUnmount 中清除。相互關(guān)聯(lián)且需要對照修改的代碼被進行了拆分,而完全不相關(guān)的代碼卻在同一個方法中組合在一起。如此很容易產(chǎn)生 bug,并且導(dǎo)致邏輯不一致。
在多數(shù)情況下,不可能將組件拆分為更小的粒度,因為狀態(tài)邏輯無處不在。這也給測試帶來了一定挑戰(zhàn)。同時,這也是很多人將 React 與狀態(tài)管理庫結(jié)合使用的原因之一。但是,這往往會引入了很多抽象概念,需要你在不同的文件之間來回切換,使得復(fù)用變得更加困難。
為了解決這個問題,Hook 將組件中相互關(guān)聯(lián)的部分拆分成更小的函數(shù)(比如設(shè)置訂閱或請求數(shù)據(jù)),而并非強制按照生命周期劃分。你還可以使用 reducer 來管理組件的內(nèi)部狀態(tài),使其更加可預(yù)測。
3. 用更少的代碼,實現(xiàn)同樣的效果下面的代碼可以直觀的體現(xiàn)出來,在某些場景下,使用 hook 來實現(xiàn)對應(yīng)的功能,可以節(jié)省大部分的代碼
3.1 清除副作用更加緊湊對比 Class 組件來說,清除副作用要簡單的多,如下代碼,在 useEffect hook 里面返回一個函數(shù),當(dāng)我們的函數(shù)組件卸載的時候,就會自動執(zhí)行這個函數(shù),從而來清除副作用。想想我們在 Class 組件里面需要在 componentWillUnmount 生命周期里面去編寫對應(yīng)的代碼。
對比兩者我們發(fā)現(xiàn),使用 useEffect 的方式,能夠?qū)燧d和卸載的邏輯更加緊密的耦合在一起,從而減少 BUG 的發(fā)生
useEffect(() => { const id = setInterval(() => { setCount(count => count + 1) }, 1000) return () => clearInterval(id) }, []) // 比如給 windows 掛載監(jiān)聽函數(shù) useEffect(() => { window.addEventListener("reszie", handleRezie) return () => { window.removeEventListener("resize", handleRezie) } }, [])如何正確的使用 Hook ? 1. 使用規(guī)則
只在最頂層使用 Hook:不要在循環(huán),條件或嵌套函數(shù)中調(diào)用 Hook, 確保總是在你的 React 函數(shù)的最頂層調(diào)用他們。
不要在普通的 JavaScript 函數(shù)中調(diào)用 Hook。你可以
[x] 在 React 的函數(shù)組件中調(diào)用 Hook
[x] 在自定義 Hook 中調(diào)用其他 Hook
2. 只有在自己依賴更新時才執(zhí)行 effect使用 useEffect 完成副作用操作,賦值給 useEffect 的函數(shù)會在組件渲染到屏幕之后;牢記這句話。
仔細觀察如下代碼,當(dāng)函數(shù)組件里面,有多個 effect 的時候,默認的 effect 將在每次 UI render 之后被調(diào)用。當(dāng)我們通過 useEffect 的第二個數(shù)組類型參數(shù),指明當(dāng)前 effect 的依賴,就能避免不相關(guān)的執(zhí)行開銷了。
通過啟用 eslint-plugin-react-hooks 插件,來強制提醒我們在使用 effect 的時候,申明所需要的依賴
{ "plugins": [ // ... "react-hooks" ], "rules": { // ... "react-hooks/rules-of-hooks": "error", "react-hooks/exhaustive-deps": "warn" } }
const CounterHook = () => { const [count, setCount] = useState(0) const [name, setName] = useState("heaven") useEffect(() => { document.title = `counterWithHook ${count}` }, [count]) useEffect(() => { console.log("you name is", name) }, [name]) return (2.1 不要忘記函數(shù)依賴) }Counter with Hook
You click {count} times
setName(e.target.value)} />
your name is {name}
對于 useEffect 內(nèi)部方法,一旦引用外部的函數(shù),那么這個時候需要注意了:
需要把 useEffect 內(nèi)部引用到的方式,聲明為當(dāng)前 effect 的依賴
在下圖的代碼中,我們可以看到,在 effect 函數(shù)內(nèi)部,引入外部的函數(shù),我們的 eslint-plugin-react-hooks 插件會自動提示我們需要把對應(yīng)的函數(shù)作為依賴添加進去
不規(guī)范示例:這里在安裝了插件的情況下,會自動提示我們將 fetchData 函數(shù)移入 effect 內(nèi)部
const getFetchUrl = () => { return `https://hn.algolia.com/api/v1/search?query=${query}` } const fetchData = async () => { return axios.get(getFetchUrl()) } useEffect(() => { fetchData().then(resp => { console.log(resp) setData(resp.data) }) }, [])
正確的寫法:
useEffect(() => { const getFetchUrl = () => { return `https://hn.algolia.com/api/v1/search?query=${query}` } const fetchData = async () => { return axios.get(getFetchUrl()) } fetchData().then(resp => { console.log(resp) setData(resp.data) }) }, [query])3、理解每一次的 Rendering
每一次渲染都有它自己的 Props and State
每一次渲染都有它自己的事件處理函數(shù)
每次渲染都有它自己的 Effects
運行如下代碼之后,在我們點擊 Show alert 按鈕之后,然后點擊 Click me 按鈕,alert 輸出的永遠是在點擊的那個時刻的 count;
換句話來說;在 hooks 組件里面,每一次的渲染,都相當(dāng)于記錄當(dāng)前次的『快照』
import React, { useEffect, useState } from "react" const Counter = () => { const [count, setCount] = useState(0) const handleAlertClick = () => { setTimeout(() => { alert(`Yout clicked me: ${count}`) }, 3000) } useEffect(() => { setTimeout(() => { console.log(`Yout clicked ${count} times`) }, 3000) }) return (使用自定義 Hook) } export default CounterYou clicked {count} times
通過自定義 Hook,可以將組件邏輯提取到可重用的函數(shù)中。
當(dāng)我們想在兩個函數(shù)之間共享邏輯時,我們會把它提取到第三個函數(shù)中。而組件和 Hook 都是函數(shù),所以也同樣適用這種方式。
自定義 Hook 是一個函數(shù),其名稱以 “use” 開頭,函數(shù)內(nèi)部可以調(diào)用其他的 Hook。
自定義 useService hookuseService.js 自定義的一個 server hook,該 hook 封裝了 ajax 請求中的 { loading, error, response } 三個基礎(chǔ)邏輯;有了這個 hook 我們就能很輕松的在每次網(wǎng)絡(luò)請求里面去處理各種異常邏輯了;詳細用法看文章最后的 Table 分頁操作實例
import { useEffect, useRef, useState, useCallback } from "react" import { isEqual } from "lodash" const useService = (service, params) => { const prevParams = useRef(null) const [callback, { loading, error, response }] = useServiceCallback(service) useEffect(() => { if (!isEqual(prevParams.current, params)) { prevParams.current = params callback(params) } }) return { loading, error, response } } const useServiceCallback = service => { const [loading, setLoading] = useState(false) const [error, setError] = useState(null) const [response, setResponse] = useState(null) // 使用 useCallback,來判斷 service 是否改變 const callback = useCallback( params => { setLoading(true) setError(null) service(params) .then(response => { console.log(response) setLoading(false) setResponse(response) }) .catch(error => { setLoading(false) setError(error) }) }, [service] ) return [callback, { loading, error, response }] }實例剖析 Table 分頁操作
如下代碼,使用 hook 的方式來實現(xiàn)表格的分頁,數(shù)據(jù)請求操作,
跑馬燈中獎使用 hook 實現(xiàn)一個簡易版的跑馬燈抽獎邏輯
參考資料官方 Hook 介紹
Hook 規(guī)則
Hook API 索引
如何在 Hook 中發(fā)起請求
useEffect 詳解
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/109624.html
摘要:起飛指南作者元瀟方凳雅集出品目前放出來了個內(nèi)置,但僅僅基于以下兩個,就能做很多事情。行代碼實現(xiàn)一個全局元瀟根組件掛上即可子組件調(diào)用隨時隨地實現(xiàn)一個局部元瀟的本質(zhì)是的一個語法糖,感興趣可以閱讀一下的類型定義和實現(xiàn)。 React Hook起飛指南 作者:元瀟 方凳雅集出品 16.8目前放出來了10個內(nèi)置hook,但僅僅基于以下兩個API,就能做很多事情。所以這篇文章不會講很多API,...
摘要:在讀了一些文章后,大致是找到自己總是掉坑的原因了沒理解中的特性。通過這個示例,相信會比較容易地理解特性,并如何使用來暫時繞過它。在知道并理解這個特性后,有助于進一步熟悉了的運行機制,減少掉坑的次數(shù)。 由于剛使用 React hooks 不久,對它的脾氣還拿捏不準(zhǔn),掉了很多次坑;這里的 坑 的意思并不是說 React hooks 的設(shè)計有問題,而是我在使用的時候,因為還沒有跟上它的理念導(dǎo)...
摘要:顧名思義,受控組件的值由控制,能為與用戶交互的元素提供值,而不受控制的元素不獲取值屬性。另外我發(fā)現(xiàn)受控組件更容易理解和于使用。只是一種把組件作為參數(shù)的函數(shù),并且與沒有包裝器的組件相比,能夠返回具有擴展功能的新組件。其中三個基本的是,和。 翻譯:瘋狂的技術(shù)宅原文:https://www.toptal.com/react/... 本文首發(fā)微信公眾號:jingchengyideng歡迎關(guān)...
摘要:以我個人的觀點,要不要使用呢建議用的的人項目版本已經(jīng)是了新建的項目一直對新技術(shù)保持關(guān)注,躍躍欲試的人對函數(shù)式編程愛好的人對的,,厭煩,甚至因為重新渲染整天在頭疼的人不建議用的人對極其厭惡,對生命周期編程是非愛好的人。 react hook發(fā)布也已經(jīng)有幾個月了,相信有部分人已經(jīng)開始使用了,還有些人在猶豫要不要用,可能更多人安于現(xiàn)狀,沒有要用的打算,甚至還有很多公司的react版本是15或...
摘要:簡介是的新增特性。我們統(tǒng)一把這些操作稱為副作用,或者簡稱為作用。由于副作用函數(shù)是在組件內(nèi)聲明的,所以它們可以訪問到組件的和。副作用函數(shù)還可以通過返回一個函數(shù)來指定如何清除副作用。目前為止,有兩種主流方案來解決這個問題高階組件和。 Hook 簡介 Hook 是 React 16.8 的新增特性。它可以讓你在不編寫 class 的情況下使用 state 以及其他的 React 特性。 us...
閱讀 2668·2023-04-26 02:44
閱讀 8573·2021-11-22 14:44
閱讀 2128·2021-09-27 13:36
閱讀 2505·2021-09-08 10:43
閱讀 688·2019-08-30 15:56
閱讀 1399·2019-08-30 15:55
閱讀 2893·2019-08-28 18:12
閱讀 2836·2019-08-26 13:50