摘要:比如在條件判斷中使用,在循環(huán),嵌套函數(shù)中使用,都會造成執(zhí)行順序不一致的問題。而比如定時器,事件監(jiān)聽。第一個參數(shù)的返回值,會在組件卸載時執(zhí)行,相當于,可以清理定時器,移除事件監(jiān)聽,取消一些訂閱。
什么是 Hooks?
不通過編寫類組件的情況下,可以在組件內(nèi)部使用狀態(tài)(state) 和其他 React 特性(生命周期,context)的技術(shù)Hooks 為什么會出現(xiàn)
在之前的 React 版本中,組件分為兩種:函數(shù)式組件(或無狀態(tài)組件(StatelessFunctionComponent))和類組件,而函數(shù)式組件是一個比較的純潔的 props => UI 的輸入、輸出關(guān)系,但是類組件由于有組件自己的內(nèi)部狀態(tài),所以其輸出就由 props 和 state 決定,類組件的輸入、輸出關(guān)系就不再那么純潔。同時也會帶來下列問題:
狀態(tài)邏輯難以復(fù)用。很多類組件都有一些類似的狀態(tài)邏輯,但是為了重用這些狀態(tài)邏輯,社區(qū)提出了 render props 或者 hoc 這些方案,但是這兩種模式對組件的侵入性太強。另外,會產(chǎn)生組件嵌套地獄的問題。
大多數(shù)開發(fā)者在編寫組件時,不管這個組件有木有內(nèi)部狀態(tài),會不會執(zhí)行生命周期函數(shù),都會將組件編寫成類組件,后續(xù)迭代可能增加了內(nèi)部狀態(tài),又增加了副作用處理,又在組件中調(diào)用了一些生命周期函數(shù),文件代碼行數(shù)日益增多,最后導(dǎo)致組件中充斥著無法管理的混亂的狀態(tài)邏輯代碼和各種副作用,各種狀態(tài)邏輯散落在實例方法和生命周期方法中,維護性變差,拆分更是難上加難。
在類組件中,需要開發(fā)者額外去關(guān)注 this 問題,事件監(jiān)聽器的添加和移除等等。
State Hookstate hook 提供了一種可以在 function component 中添加狀態(tài)的方式。通過 state hook,可以抽取狀態(tài)邏輯,使組件變得可測試,可重用。開發(fā)者可以在不改變組件層次結(jié)構(gòu)的情況下,去重用狀態(tài)邏輯。更好的實現(xiàn)關(guān)注點分離。
一個簡單的使用 useState 栗子
import React, { useState } from "react"; const StateHook = () => { const [count, setCount] = useState(0); return (); };you clicked {count} times
幾點說明:
useState 推薦一種更加細粒度的控制狀態(tài)的方式,即一個狀態(tài)對應(yīng)一個狀態(tài)設(shè)置函數(shù),其接受的參數(shù)將作為這個狀態(tài)的初始值。其返回一個長度為2的元組,第一項為當前狀態(tài),第二項為更新函數(shù)。
useState 的執(zhí)行順序在每一次更新渲染時必須保持一致,否則多個 useState 調(diào)用將不會得到各自獨立的狀態(tài),也會造成狀態(tài)對應(yīng)混亂。比如在條件判斷中使用 hook,在循環(huán),嵌套函數(shù)中使用 hook,都會造成 hook 執(zhí)行順序不一致的問題。最后導(dǎo)致狀態(tài)的混亂。另外,所有的狀態(tài)聲明都應(yīng)該放在函數(shù)頂部,首先聲明。
useState 和 setState 的區(qū)別
useState 將 setState 進行覆蓋式更新,而 setState 則將狀態(tài)進行合并式更新。
一個不正確的栗子
import React, { useState, ChangeEvent } from "react"; const UserForm = () => { const [state, setUser] = useState({ name: "", email: "" }); const { name, email } = state; const handleNameChange = (event: ChangeEvent) => { const { target: { value: name } } = event; // 這里不可以多帶帶的設(shè)置某一個字段 新的狀態(tài)必須與初始的狀態(tài)類型保持一致 // 如果只設(shè)置了其中一個字段,編譯器會報錯,同時其余的字段也會丟失 setUser({ name, email }); }; const handleEmailChange = (event: ChangeEvent ) => { const { target: { value: email } } = event; // 這里不可以多帶帶的設(shè)置某一個字段 新的狀態(tài)必須與初始的狀態(tài)類型保持一致 setUser({ name, email }); }; return ( <> > ); }
正確的做法
import React, { useState, ChangeEvent } from "react"; const UserForm = () => { // 一個狀態(tài)對應(yīng)一個狀態(tài)更新函數(shù) const [name, setName] = useState(""); const [email, setEmail] = useState(""); const handleNameChange = (event: ChangeEventEffect Hook) => { const { target: { value: name } } = event; // hear could do some validation setName(name); }; const handleEmailChange = (event: ChangeEvent ) => { const { target: { value: email } } = event; // hear could do some validation setEmail(email); }; return ( <> > ); }
數(shù)據(jù)獲取,設(shè)置訂閱,手動的更改 DOM,都可以稱為副作用,可以將副作用分為兩種,一種是需要清理的,另外一種是不需要清理的。比如網(wǎng)絡(luò)請求,DOM 更改,日志這些副作用都不要清理。而比如定時器,事件監(jiān)聽。
一個簡單使用 effect hook 去修改文檔標題的栗子。
import React, { useState, useEffect } from "react"; const effectHook = () => { const [count, setCount] = useState(0); useEffect(() => { document.title = `you clicked ${count} times`; }, [count]); return (); };you clicked {count} times
在調(diào)用 useEffect 后,相當于告訴 React 在每一次組件更新完成渲染后,都調(diào)用傳入 useEffect 中的函數(shù),包括初次渲染以及后續(xù)的每一次更新渲染。
幾點說明:
useEffect(effectCallback: () => void, deps: any[]) 接收兩個參數(shù),第二個參數(shù)依賴項是可選的,表示這個 effect 要依賴哪些值。
有時候我們并不想每次渲染 effect 都執(zhí)行,只有某些值發(fā)生變化才去執(zhí)行 effect,這個時候我們可以指定這個 effect 的依賴列表,可以是一個也可以幾個,當其中列表中的某一個值發(fā)生變化,effect 才會執(zhí)行。
第一個參數(shù)的返回值,會在組件卸載時執(zhí)行,相當于 componentWillUnmount,可以清理定時器,移除事件監(jiān)聽,取消一些訂閱。
當?shù)诙€參數(shù)為一個空數(shù)組時,相當于 componentDidMount 和 componentWillUnmount,表明這個 effect 沒有任何依賴,只在首次渲染時執(zhí)行。
Custom Hook也可以使用 useEffect 和 useState 實現(xiàn)自定義 hook。
一個給 DOM 元素添加事件監(jiān)聽器的栗子。
import { useRef, useEffect } from "react"; type EventType = keyof HTMLElementEventMap; type Handler = (event: Event) => void; const useEventListener = ( eventName: EventType, handler: Handler, element: EventTarget = document ) => { // 這里使用 `useRef` 來保存?zhèn)魅氲谋O(jiān)聽器, // 在監(jiān)聽器變更后,去更新 `useRef` 返回的對象的 `current` 屬性 const saveHandler = useRef(); useEffect(() => { saveHandler.current = handler; }, [handler]); useEffect(() => { const supported = element && element.addEventListener; if (!supported) { return; } const listener: Handler = (event: Event) => (saveHandler.current as Handler)(event); element.addEventListener(eventName, listener); return () => { element.removeEventListener(eventName, listener); }; }, [eventName, element]); }
一個使用 useReducer 來實現(xiàn)加、減計數(shù)器的栗子。這里雖然使用 useReducer 創(chuàng)建了類似 redux 的 功能,但是如果有多個組件都引用了這個 hook,那么這個 hook 提供的狀態(tài)是相互獨立、互不影響的,即 useReducer 只提供了狀態(tài)管理,但是并沒有提供數(shù)據(jù)持久化的功能。redux 卻提供了一種全局維護同一個數(shù)據(jù)源的機制。所以可以利用 useReducer 和 Context 來實現(xiàn)數(shù)據(jù)持久化的功能。
import React, { useReducer } from "react"; const INCREMENT = "increment"; const DECREMENT = "decrement"; const initHandle = (initCount) => { return { count: initCount }; }; const reducer = (state, action) => { switch (action.type) { case INCREMENT: return { count: state.count + 1 }; case DECREMENT: return { count: state.count - 1 }; case "reset": return { count: action.payload }; default: return state; } }; const Counter = ({ initialCount }) => { const [state, dispatch] = useReducer(reducer, initialCount, initHandle); const { count } = state; return (Counter: {count} j); };
一個對封裝數(shù)據(jù)請求栗子。
import { useState, useEffect } from "react"; import axios, { AxiosRequestConfig } from "axios"; interface RequestError { error: null | boolean; message: string; } const requestError: RequestError = { error: null, message: "", }; /** * @param url request url * @param initValue if initValue changed, the request will send again * @param options request config data * * @returns a object contains response"s data, request loading and request error */ const useFetchData = (url: string, initValue: any, options: AxiosRequestConfig = {}) => { const [data, saveData] = useState(); const [loading, updateLoading] = useState(false); const [error, updateError] = useState(requestError); let ignore = false; const fetchData = async () => { updateLoading(true); const response = await axios(url, options); if (!ignore) saveData(response.data); updateLoading(false); }; useEffect(() => { try { fetchData(); } catch (error) { updateError({ error: true, message: error.message }); } return () => { ignore = true; }; }, [initValue]); return { data, loading, error }; }; export { useFetchData };Rules of Hook
隨來 hooks 帶來了新的組件編寫范式,但是下面兩條規(guī)則還是要開發(fā)者注意的。
在頂部使用 hook,不要使用 hook 在條件判斷,循環(huán),嵌套函數(shù)。
只在 function component 中使用 hook,或者自定義 hook 中使用 hook, 不要在常規(guī)的 JavaScript 函數(shù)中使用 hook
新的問題hooks 的帶來,雖然解決之前存在的一些問題,但是也帶來了新的問題。
異常捕獲。之前的版本中,我們可以用 componentDidCatch 來捕獲組件作用域內(nèi)的異常,做一些提示。但是在 hooks 中 ,我們只能使用 try {} catch(){} ` 去捕獲,使用姿勢也比較別扭。
一個組件若有狀態(tài),則狀態(tài)一旦改變,所有的子組件需要重新渲染。所以一個有狀態(tài)的組件,應(yīng)該是沒有子組件的。即 有狀態(tài)的組件不做渲染,有渲染的組件沒有狀態(tài)。
狀態(tài)變更的函數(shù)不支持回調(diào)。this.setState() 中支持第二個參數(shù),允許我們在狀態(tài)變更后,傳入回調(diào)函數(shù)做一些其他事情。但是 useState 不支持。詳見。
參考鏈接
making-sense-of-react-hooks
rehooks
awesome-react-hooks
如何使用useEffect來獲取數(shù)據(jù)
hooks 是如何工作的
更多關(guān)于 hooks 的討論
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/104060.html
摘要:前言一直對這個新特性非常感興趣,終于今天有時間,花了大半天時間,把的官方教程過了一遍,收獲頗多,驚嘆這個新特性真好用,以后開發(fā)用這個怕是要起飛了 前言:一直對這個新特性非常感興趣,終于今天有時間,花了大半天時間,把 Hooks的官方教程過了一遍,收獲頗多,驚嘆這個新特性真 TM 好用,以后開發(fā)用這個怕是要起飛了
摘要:使用完成副作用操作,賦值給的函數(shù)會在組件渲染到屏幕之后。如此很容易產(chǎn)生,并且導(dǎo)致邏輯不一致。同時,這也是很多人將與狀態(tài)管理庫結(jié)合使用的原因之一。當我們通過的第二個數(shù)組類型參數(shù),指明當前的依賴,就能避免不相關(guān)的執(zhí)行開銷了。 前言 本文內(nèi)容大部分參考了 overreacted.io 博客一文,同時結(jié)合 React Hook 官方 文章,整理并歸納一些筆記和輸出個人的一些理解 什么是 Hoo...
摘要:的來源鉤子,顧名思義,為了解決在函數(shù)組件中使用和生命周期,同時提高業(yè)務(wù)邏輯復(fù)用。函數(shù)組件等同于一個純的專門用作渲染的函數(shù),我們知道,在函數(shù)組件中,我們無法使用和生命周期,這也是為了解決的問題。 Hooks的來源 Hooks => 鉤子,顧名思義,為了解決在函數(shù)組件(Function Component)中使用state和生命周期,同時提高業(yè)務(wù)邏輯復(fù)用。 Function Co...
摘要:基于與網(wǎng)易云音樂開發(fā),技術(shù)棧主要是目前主要是著重小程序端的展示,主要也是借此項目強化下上述幾個技術(shù)棧的使用,通過這個項目也可以幫助你快速使用開發(fā)一個屬于你自己的小程序地址,感興趣的話可以關(guān)注下,功能會進行持續(xù)完善快速開始首先需要在目錄下 基于Taro與網(wǎng)易云音樂api開發(fā),技術(shù)棧主要是:typescript+taro+taro-ui+redux,目前主要是著重小程序端的展示,主要也是借...
摘要:此優(yōu)化有助于避免在每個渲染上進行昂貴的計算。同樣也是一個函數(shù),接受兩個參數(shù),第一個參數(shù)為函數(shù),第二個參數(shù)為要比對的值,返回一個值。同理,第二個參數(shù)傳入的值沒有更新時,不會執(zhí)行。以上代碼的地址為初體驗 什么是Hooks?Hooks是react即將推出的功能,它允許您在不編寫類的情況下使用狀態(tài)和其他React功能。我的理解就是可以用寫無狀態(tài)組件的方式去編寫擁有狀態(tài)的組件。遺憾的是,正式版1...
閱讀 1866·2023-04-26 01:41
閱讀 3169·2021-11-23 09:51
閱讀 2819·2021-10-09 09:43
閱讀 9349·2021-09-22 15:13
閱讀 2525·2021-09-07 09:59
閱讀 2686·2019-08-30 15:44
閱讀 1195·2019-08-30 12:45
閱讀 2679·2019-08-30 12:43