摘要:第一次了解這項特性的時候,真的有一種豁然開朗,發現新大陸的感覺。為了解決這一痛點,才會有剪頭函數的綁定特性。它同時具備和三個生命周期函數的執行時機。
歡迎關注我的公眾號睿Talk,獲取我最新的文章:
React Hooks 是從 v16.8 引入的又一開創性的新特性。第一次了解這項特性的時候,真的有一種豁然開朗,發現新大陸的感覺。我深深的為 React 團隊天馬行空的創造力和精益求精的鉆研精神所折服。本文除了介紹具體的用法外,還會分析背后的邏輯和使用時候的注意事項,力求做到知其然也知其所以然。
這個系列分上下兩篇,這里是下篇的傳送門:
React Hooks 解析(下):進階
Hooks的出現是為了解決 React 長久以來存在的一些問題:
帶組件狀態的邏輯很難重用
為了解決這個問題,需要引入render props或higher-order components這樣的設計模式,如react-redux提供的connect方法。這種方案不夠直觀,而且需要改變組件的層級結構,極端情況下會有多個wrapper嵌套調用的情況。
Hooks可以在不改變組件層級關系的前提下,方便的重用帶狀態的邏輯。
復雜組件難于理解
大量的業務邏輯需要放在componentDidMount和componentDidUpdate等生命周期函數中,而且往往一個生命周期函數中會包含多個不相關的業務邏輯,如日志記錄和數據請求會同時放在componentDidMount中。另一方面,相關的業務邏輯也有可能會放在不同的生命周期函數中,如組件掛載的時候訂閱事件,卸載的時候取消訂閱,就需要同時在componentDidMount和componentWillUnmount中寫相關邏輯。
Hooks可以封裝相關聯的業務邏輯,讓代碼結構更加清晰。
難于理解的 Class 組件
JS 中的this關鍵字讓不少人吃過苦頭,它的取值與其它面向對象語言都不一樣,是在運行時決定的。為了解決這一痛點,才會有剪頭函數的this綁定特性。另外 React 中還有Class Component和Function Component的概念,什么時候應該用什么組件也是一件糾結的事情。代碼優化方面,對Class Component進行預編譯和壓縮會比普通函數困難得多,而且還容易出問題。
Hooks可以在不引入 Class 的前提下,使用 React 的各種特性。
三、什么是 HooksHooks are functions that let you “hook into” React state and lifecycle features from function components
上面是官方解釋。從中可以看出 Hooks 是函數,有多個種類,每個 Hook 都為Function Component提供使用 React 狀態和生命周期特性的通道。Hooks 不能在Class Component中使用。
React 提供了一些預定義好的 Hooks 供我們使用,下面我們來詳細了解一下。
四、State Hook先來看一個傳統的Class Component:
class Example extends React.Component { constructor(props) { super(props); this.state = { count: 0 }; } render() { return (); } }You clicked {this.state.count} times
使用 State Hook 來改寫會是這個樣子:
import React, { useState } from "react"; function Example() { // 定義一個 State 變量,變量值可以通過 setCount 來改變 const [count, setCount] = useState(0); return (); }You clicked {count} times
可以看到useState的入參只有一個,就是 state 的初始值。這個初始值可以是一個數字、字符串或對象,甚至可以是一個函數。當入參是一個函數的時候,這個函數只會在這個組件初始渲染的時候執行:
const [state, setState] = useState(() => { const initialState = someExpensiveComputation(props); return initialState; });
useState的返回值是一個數組,數組的第一個元素是 state 當前的值,第二個元素是改變 state 的方法。這兩個變量的命名不需要遵守什么約定,可以自由發揮。要注意的是如果 state 是一個對象,setState 的時候不會像Class Component的 setState 那樣自動合并對象。要達到這種效果,可以這么做:
setState(prevState => { // Object.assign 也可以 return {...prevState, ...updatedValues}; });
從上面的代碼可以看出,setState 的參數除了數字、字符串或對象,還可以是函數。當需要根據之前的狀態來計算出當前狀態值的時候,就需要傳入函數了,這跟Class Component的 setState 有點像。
另外一個跟Class Component的 setState 很像的一點是,當新傳入的值跟之前的值一樣時(使用Object.is比較),不會觸發更新。
五、Effect Hook解釋這個 Hook 之前先理解下什么是副作用。網絡請求、訂閱某個模塊或者 DOM 操作都是副作用的例子,Effect Hook 是專門用來處理副作用的。正常情況下,在Function Component的函數體中,是不建議寫副作用代碼的,否則容易出 bug。
下面的Class Component例子中,副作用代碼寫在了componentDidMount和componentDidUpdate中:
class Example extends React.Component { constructor(props) { super(props); this.state = { count: 0 }; } componentDidMount() { document.title = `You clicked ${this.state.count} times`; } componentDidUpdate() { document.title = `You clicked ${this.state.count} times`; } render() { return (); } }You clicked {this.state.count} times
可以看到componentDidMount和componentDidUpdate中的代碼是一樣的。而使用 Effect Hook 來改寫就不會有這個問題:
import React, { useState, useEffect } from "react"; function Example() { const [count, setCount] = useState(0); useEffect(() => { document.title = `You clicked ${count} times`; }); return (); }You clicked {count} times
useEffect會在每次 DOM 渲染后執行,不會阻塞頁面渲染。它同時具備componentDidMount、componentDidUpdate和componentWillUnmount三個生命周期函數的執行時機。
此外還有一些副作用需要組件卸載的時候做一些額外的清理工作的,例如訂閱某個功能:
class FriendStatus extends React.Component { constructor(props) { super(props); this.state = { isOnline: null }; this.handleStatusChange = this.handleStatusChange.bind(this); } componentDidMount() { ChatAPI.subscribeToFriendStatus( this.props.friend.id, this.handleStatusChange ); } componentWillUnmount() { ChatAPI.unsubscribeFromFriendStatus( this.props.friend.id, this.handleStatusChange ); } handleStatusChange(status) { this.setState({ isOnline: status.isOnline }); } render() { if (this.state.isOnline === null) { return "Loading..."; } return this.state.isOnline ? "Online" : "Offline"; } }
在componentDidMount訂閱后,需要在componentWillUnmount取消訂閱。使用 Effect Hook 來改寫會是這個樣子:
import React, { useState, useEffect } from "react"; function FriendStatus(props) { const [isOnline, setIsOnline] = useState(null); useEffect(() => { function handleStatusChange(status) { setIsOnline(status.isOnline); } ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange); // 返回一個函數來進行額外的清理工作: return function cleanup() { ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange); }; }); if (isOnline === null) { return "Loading..."; } return isOnline ? "Online" : "Offline"; }
當useEffect的返回值是一個函數的時候,React 會在下一次執行這個副作用之前執行一遍清理工作,整個組件的生命周期流程可以這么理解:
組件掛載 --> 執行副作用 --> 組件更新 --> 執行清理函數 --> 執行副作用 --> 組件更新 --> 執行清理函數 --> 組件卸載
上文提到useEffect會在每次渲染后執行,但有的情況下我們希望只有在 state 或 props 改變的情況下才執行。如果是Class Component,我們會這么做:
componentDidUpdate(prevProps, prevState) { if (prevState.count !== this.state.count) { document.title = `You clicked ${this.state.count} times`; } }
使用 Hook 的時候,我們只需要傳入第二個參數:
useEffect(() => { document.title = `You clicked ${count} times`; }, [count]); // 只有在 count 改變的時候才執行 Effect
第二個參數是一個數組,可以傳多個值,一般會將 Effect 用到的所有 props 和 state 都傳進去。
當副作用只需要在組件掛載的時候和卸載的時候執行,第二個參數可以傳一個空數組[],實現的效果有點類似componentDidMount和componentWillUnmount的組合。
六、總結本文介紹了在 React 之前版本中存在的一些問題,然后引入 Hooks 的解決方案,并詳細介紹了 2 個最重要的 Hooks:useState和useEffect的用法及注意事項。本來想一篇寫完所有相關的內容,但發現坑有點深,只能分兩次填了:)
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/103688.html
摘要:第一次了解這項特性的時候,真的有一種豁然開朗,發現新大陸的感覺。在絕大多數情況下,是更好的選擇。唯一例外的就是需要根據新的來進行操作的場景。會保證在頁面渲染前執行,也就是說頁面渲染出來的是最終的效果。上面條規則都是為了保證調用順序的穩定性。 歡迎關注我的公眾號睿Talk,獲取我最新的文章:showImg(https://segmentfault.com/img/bVbmYjo); 一、...
摘要:動態處理與,封裝了在運行時的進行一類增加和刪除的操作,例如可以再切換到某一路由時動態的加入一個個人猜測,熱更新很有可能也利用了這個兩個與。以上是本人對于的粗略的理解,內容如有錯誤,還請大家指出。 寫在前面 dva是螞蟻金服推出的一個單頁應用框架,對redux,react-router,redux-saga進行了上層封裝,沒有引入新的概念,但是極大的程度上提升了開發效率;下面內容為本人理...
這是講 ahooks 源碼的第一篇文章,簡要就是以下幾點: 加深對 React hooks 的理解。 學習如何抽象自定義 hooks。構建屬于自己的 React hooks 工具庫。 培養閱讀學習源碼的習慣,工具庫是一個對源碼閱讀不錯的選擇。 注:本系列對 ahooks 的源碼解析是基于v3.3.13。自己 folk 了一份源碼,主要是對源碼做了一些解讀,可見詳情。 第一篇主要介紹 a...
起因 社會在不斷的向前,技術也在不斷的完善進步。從 React Hooks 正式發布到現在,越來越多的項目正在使用 Function Component 替代 Class Component,Hooks 這一新特性也逐漸被廣泛的使用。 這樣的解析是不是很熟悉,在日常中時常都有用到,但也有一個可以解決這樣重復的就是對數據請求的邏輯處理,對防抖節流的邏輯處理等。 另一方面,由于 Hoo...
摘要:本文將根據以下章節分別梳理每個鉤子同步鉤子首先安裝是簡單的同步鉤子,它很類似于發布訂閱。至此,我們把的所有同步鉤子都解析完畢異步鉤子比同步鉤子麻煩些,我們會在下一章節開始解析異步的鉤子傳送門深入理解核心模塊鉤子異步版代碼 記錄下自己在前端路上爬坑的經歷 加深印象,正文開始~ tapable是webpack的核心依賴庫 想要讀懂webpack源碼 就必須首先熟悉tapableok.下面是...
閱讀 2293·2021-11-24 09:39
閱讀 2535·2021-11-22 15:24
閱讀 2976·2021-09-02 09:48
閱讀 3010·2021-07-26 22:01
閱讀 1433·2019-08-30 11:09
閱讀 1673·2019-08-29 18:47
閱讀 601·2019-08-29 15:40
閱讀 2132·2019-08-29 15:22