react 數組新增_React 新特性 Hooks 講解及實例(二)

本文是?React?新特性系列的第二篇,第一篇請點擊這里:

React 新特性講解及實例

什么是 Hooks

Hook 是 React 16.8 的新增特性。它可以讓你在不編寫 類組件 的情況下使用 state以及其他的 React 特性。

類組件的不足

狀態邏輯復用難

  • 缺少復用機制

  • 渲染屬性和高階組件導致層級冗余

趨向復雜難以維護

  • 生命周期函數混雜不相干邏輯

  • 相干邏輯分散在不同生命周期

this 指向困擾

  • 內聯函數過度創建新句柄

  • 類成員函數不能保證?this

Hooks 優勢

優化類組件的三大問題

  • 函數組件無 this 問題

  • 自定義 Hook 方便復用狀態邏輯

  • 副作用的關注點分離

使用 State Hook

import React, {Component} from 'react'

class App extends Component {

state = {

count: 0

};

render() {

const {count} = this.state;

return (

<button type="button"

onClick={() => {

this.setState({

count: count + 1

})

}}

>Click({count})button>

)

}

}

export default App;

以上代碼很好理解,點擊按鈕讓 count 值加 1

接下來我們使用 useState 來實現上述功能。

import React, {useState} from 'react'

function App () {

const [count, setCount] = useState(0)

return (

<button type="button"

onClick={() => {setCount(count + 1) }}

>Click({count})button>

)

}

在這里, useState 就是一個 Hook。通過在函數組件里調用它來給組件添加一些內部 state,React 會在重復渲染時保留這個 state

useState 會返回一對值:當前狀態和一個讓你更新它的函數。你可以在事件處理函數中或其他一些地方調用這個函數。它類似 class 組件的 this.setState,但是它不會把新的 state 和舊的 state 進行合并。 useState 唯一的參數就是初始 state

useState 讓代碼看起來簡潔了,但是我們可能會對組件中,直接調用 useState 返回的狀態會有些懵。既然 userState 沒有傳入任何的環境參數,它怎么知道要返回的的是 count 的呢,而且還是這個組件的 count 不是其它組件的 count

初淺的理解: useState 確實不知道我們要返回的 count,但其實也不需要知道,它只要返回一個變量就行了。數組解構的語法讓我們在調用 useState 時可以給 state 變量取不同的名字。

useState 怎么知道要返回當前組件的 state?

因為 JavaScript 是單線程的。在 useState 被調用時,它只能在唯一一個組件的上下文中。

有人可能會問,如果組件內有多個 usreState,那 useState 怎么知道哪一次調用返回哪一個 state 呢?

這個就是按照第一次運行的次序來順序來返回的。

接著上面的例子我們在聲明一個 useState:

...

const [count, setScount] = useState(0)

const [name, setName] = useState('小智')

...

然后我們就可以斷定,以后 APP組件每次渲染的時候, useState 第一次調用一定是返回 count,第二次調用一定是返回 name。不信的話來做個實驗:

let id = 0

function App () {

let name,setName;

let count,setCount;

id += 1;

if (id & 1) {

// 奇數

[count, setCount] = useState(0)

[name, setName] = useState('小智')

} else {

// 偶數

[name, setName] = useState('小智')

[count, setCount] = useState(0)

}

return (

<button type="button"

onClick={() => {setCount(count + 1) }}

>

Click({count}), name ({name})

button>

)

}

首先在外部聲明一個 id,當 id為奇數和偶數的時候分別讓 useState 調用方式相反,運行會看到有趣的現象。

acc2357c820ab6df682da19f6431c8d1.gif

當前版本如果寫的順序不一致就會報錯。

會發現 countname 的取值串了。我們希望給 count 加 1,現在卻給 name 加了 1,說明 setCount 函數也串成了 setName 函數。

為了防止我們使用 useState 不當,React 提供了一個 ESlint 插件幫助我們檢查。

  • eslint-plugin-react-hooks

優化點

通過上述我們知道 useState 有個默認值,因為是默認值,所以在不同的渲染周期去傳入不同的值是沒有意義的,只有第一次傳入的才有效。如下所示:

...

const defaultCount = props.defaultCount || 0

const [count, setCount] = useState(defaultCount)

...

state 的默認值是基于 props,在 APP 組件每次渲染的時候 constdefaultCount=props.defaultCount||0 都會運行一次,如果它復雜度比較高的話,那么浪費的資料肯定是可觀的。

useState 支持傳入函數,來延遲初始化:

const [count, setCount] = useState(() => {

return props.defaultCount || 0

})

使用 Effect Hook

Effect Hook 可以讓你在函數組件中執行副作用操作。數據獲取,設置訂閱以及手動更改 React 組件中的 DOM 都屬于副作用。不管你知不知道這些操作,或是"副作用"這個名字,應該都在組件中使用過它們。

副作用的時機

  • Mount 之后 對應 componentDidMount

  • Update 之后 對應 componentDidUpdate

  • Unmount 之前 對應 componentWillUnmount

現在使用 useEffect 就可以覆蓋上述的情況。

為什么一個 useEffect 就能涵蓋 Mount,Update,Unmount 等場景呢。

useEffect 標準上是在組件每次渲染之后調用,并且會根據自定義狀態來決定是否調用還是不調用。

第一次調用就相當于 componentDidMount,后面的調用相當于 componentDidUpdateuseEffect 還可以返回另一個回調函數,這個函數的執行時機很重要。作用是清除上一次副作用遺留下來的狀態。

eb55b08ff540692fab0befd53fa6dfe5.png

比如一個組件在第三次,第五次,第七次渲染后執行了 useEffect 邏輯,那么回調函數就會在第四次,第六次和第八次渲染之前執行。嚴格來講,是在前一次的渲染視圖清除之前。如果 useEffect 是在第一次調用的,那么它返回的回調函數就只會在組件卸載之前調用了,也就是 componentWillUnmount

如果你熟悉 React class 的生命周期函數,你可以把 useEffect Hook 看做 componentDidMountcomponentDidUpdatecomponentWillUnmount 這三個函數的組合。

舉粟說明一下:

class App extends Component {

state = {

count: 0,

size: {

width: document.documentElement.clientWidth,

height: document.documentElement.clientHeight

}

};

onResize = () => {

this.setState({

size: {

width: document.documentElement.clientWidth,

height: document.documentElement.clientHeight

}

})

}

componentDidMount () {

document.title = this.state.count;

window.addEventListener('resize', this.onResize, false)

}

componentWillMount () {

window.removeEventListener('resize', this.onResize, false)

}

componentDidUpdate () {

document.title = this.state.count;

}

render() {

const {count, size} = this.state;

return (

<button type="button"

onClick={() => {this.setState({count: count + 1})}}

>

Click({count})

size: {size.width}x{size.height}

button>

)

}

}

上面主要做的就是網頁 title 顯示 count 值,并監聽網頁大小的變化。這里用到了 componentDidMountcomponentDidUpdate 等副作用,因為第一次掛載我們需要把初始值給 title, 當 count 變化時,把變化后的值給它 title,這樣 title 才能實時的更新。

注意,我們需要在兩個生命周期函數中編寫重復的代碼。

這邊我們容易出錯的地方就是在組件結束之后要記住銷毀事件的注冊,不然會導致資源的泄漏。現在我們把 App 組件的副作用用 useEffect 實現。

function App (props) {

const [count, setCount] = useState(0);

const [size, setSize] = useState({

width: document.documentElement.clientWidth,

height: document.documentElement.clientHeight

});

const onResize = () => {

setSize({

width: document.documentElement.clientWidth,

height: document.documentElement.clientHeight

}

)

}

useEffect(() => {

document.title = count;

})

useEffect(() => {

window.addEventListener('resize', onResize, false);

return () => {

window.removeEventListener('resize', onResize, false)

}

}, [])

return (

<button type="button"

onClick={() => {setCount(count + 1) }}

>

Click({count})

size: {size.width}x{size.height}

button>

)

}

對于上述代碼的第一個 useEffect,相比類組件,Hooks 不在關心是 mount 還是 update。用 useEffect統一在渲染后調用,就完整追蹤了 count 的值。

對于第二個 useEffect,我們可以通過返回一個回調函數來注銷事件的注冊。回調函數在視圖被銷毀之前觸發,銷毀的原因有兩種:重新渲染和組件卸載

這邊有個問題,既然 useEffect 每次渲染后都執行,那我們每次都要綁定和解綁事件嗎?當然是完全不需要,只要使用 useEffect 第二個參數,并傳入一個空數組即可。第二個參數是一個可選的數組參數,只有數組的每一項都不變的情況下, useEffect才不會執行。第一次渲染之后,useEffect 肯定會執行。由于我們傳入的空數組,空數組與空數組是相同的,因此 useEffect 只會在第一次執行一次。

這也說明我們把 resize 相關的邏輯放在一直寫,不在像類組件那樣分散在兩個不同的生命周期內。同時我們處理 title 的邏輯與 resize 的邏輯分別在兩個 useEffect 內處理,實現關注點分離。

我們在定義一個 useEffect,來看看通過不同參數,第二個參數的不同作用。

...

useEffect(() => {

console.log('count:', count)

}, [count])

...

第二個參數我們傳入 [count], 表示只有 count 的變化時,我才打印 count 值, resize 變化不會打印。

運行效果如下:

6410bdcbc967efc2ddbdcc758533bb75.gif

第二個參數的三種形態, undefined,空數組及非空數組,我們都經歷過了,但是咱們沒有看到過回調函數的執行。

現在有一種場景就是在組件中訪問 Dom 元素,在 Dom元素上綁定事件,在上述的代碼中添加以下代碼:

...

const onClick = () => {

console.log('click');

}

useEffect(() => {

document.querySelector('#size').addEventListener('click', onClick, false);

},[])

return (

...

<span id="size">size: {size.width}x{size.height}span>

div>

)

新增一個 DOM 元素,在新的 useEffect 中監聽 span 元素的點擊事件。

運行效果:

04babe3f6436e8f10ba262d67ec4b4c9.gif

假如我們 span 元素可以被銷毀重建,我們看看會發生什么情況,改造一下代碼:

return (

...

button>

{

count%2

? <span id="size">我是spanspan>

: <p id='size'>我是pp>

}

div>

運行效果:

4d0266c81f5ffed4318c6ba137cbb43c.gif

可以看出一旦 dom 元素被替換,我們綁定的事件就失效了,所以咱們始終要追蹤這個dom 元素的最新狀態。

使用 useEffect ,最合適的方式就是使用回調函數來處理了,同時要保證每次渲染后都要重新運行,所以不能給第二次參數設置 [],改造如下:

useEffect(() => {

document.querySelector('#size').addEventListener('click', onClick, false);

return () => {

document.querySelector('#size').removeEventListener('click', onClick, false);

}

})

運行結果:

2e7066a2755eb6251ed02e8e198f127d.gif

參考

[React 官方文檔][9]

《React勁爆新特性Hooks 重構去哪兒網》

交流

我是小智,公眾號「大遷世界」作者,對前端技術保持學習愛好者。我會經常分享自己所學所看的干貨,在進階的路上,共勉!

關注公眾號,后臺回復福利,即可看到福利,你懂的。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/281954.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/281954.shtml
英文地址,請注明出處:http://en.pswp.cn/news/281954.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

智課雅思詞匯---二十二、-al即是名詞性后綴又是形容詞后綴

智課雅思詞匯---二十二、-al即是名詞性后綴又是形容詞后綴 一、總結 一句話總結&#xff1a; 后綴&#xff1a;-al ②[名詞后綴] 1、構成抽象名詞&#xff0c;表示行為、狀況、事情 refusal 拒絕 proposal 提議 withdrawal 撤退 1、名詞性后綴acy是什么意思&#xff1f; 后綴&a…

javascript事件處理程序

javascript 事件處理程序 1、普通事件處理程序 <input type"button" value"click me" οnclick"showMessage()" /> function showMessage(){alert("clicked");} 2、DOMO 級事件處理程序 <span style"white-space:pre&…

eclipse新發現功能之dos和terminal(ssh連接)

dos功能&#xff1a; window——》show view——》other——》remote systems&#xff0c;選擇remote shell&#xff0c;選擇確定或者雙擊&#xff0c;打開了一個新工具窗口。點擊remote shell窗口最右上角的小三角&#xff0c;在launch子菜單中選擇local&#xff0c;點擊即可。…

7天學會python_7天學會Python最佳可視化工具Seaborn(五):結構化展示多維數據

當探索具有中等數量(不多不少的意思……)維度的數據集時&#xff0c;一個很好的方式是基于不同的子數據集構建不同的實例&#xff0c;并將它們以網格的方式組織在一張圖之中。這種技術有時被稱為“lattice”或“trellis”(大概是格子圖、網格圖)&#xff0c;這跟“small multip…

面對峰值響應沖擊,解決高并發的三大策略

2019獨角獸企業重金招聘Python工程師標準>>> 當前在互聯網的大潮下&#xff0c;眾所周知淘寶、京東這些交易系統每天產生的數據量都是海量的&#xff0c;每天的交易并發也是驚人的&#xff0c;尤其是“雙11”、“6.18”這些活動&#xff0c;對系統的峰值響應提出了非…

.NET 采用 SkiaSharp 生成二維碼和圖形驗證碼及圖片進行指定區域截取方法實現

在最新版的 .NET 平臺中&#xff0c;微軟在逐步放棄 System.Drawing.Imaging &#xff0c;給出的理由如下&#xff1a;System.Drawing命名空間對某些操作系統和應用程序類型有一些限制。在Windows&#xff0c; System.Drawing 依賴于GDI操作系統附帶的本機庫。 某些Windows SKU…

Linux運維人員必會開源運維工具體系

新手必會用深&#xff08;8-15k&#xff09;標記&#xff0c;老鳥必會深淺藍色(15-25K)標記操作系統&#xff1a;Centos,Ubuntu,Redhat,suse,Freebsd網站服務&#xff1a;nginx,apache,lighttpd,php,tomcat,resin數據 庫&#xff1a;MySQL,MariaDB,PostgreSQLDB中間件&#x…

unity讀取Text

sing UnityEngine;using System.Collections;using System.IO; //需要導入System.IO&#xff0c;主要使用它的File類public class TextTest : MonoBehaviour { private string Mytxt; //用來存放文本內容 void Start() { Mytxt ReadFile("C:\\Users\\Admin\\Desktop\\測試…

hibernate mysql 主從_MYSQL主從復制和寫分離

基礎篇https://edu.51cto.com/course/19845.htmlhttps://edu.51cto.com/course/19845.htmlhttps://edu.51cto.com/course/19841.htmlhttps://edu.51cto.com/course/21197.htmlhttps://edu.51cto.com/course/19886.htmlhttps://edu.51cto.com/course/19887.htmlhttps://edu.51ct…

深入剖析Redis系列(五) - Redis數據結構之字符串

前言 字符串類型 是 Redis 最基礎的數據結構。字符串類型 的值實際可以是 字符串&#xff08;簡單 和 復雜 的字符串&#xff0c;例如 JSON、XML&#xff09;、數字&#xff08;整數、浮點數&#xff09;&#xff0c;甚至是 二進制&#xff08;圖片、音頻、視頻&#xff09;&am…

全新升級的AOP框架Dora.Interception[6]: 框架設計和實現原理

本系列前面的五篇文章主要介紹Dora.Interception的編程模式以及對它的擴展定制&#xff0c;現在我們來聊聊它的設計和實現原理。目錄一、調用鏈抽象二、基于約定的攔截器定義三、基于調用上下文的依賴注入容器四、攔截器的提供五、調用鏈的構建六、方法攔截的實現原理七、依賴注…

activemq 安全連接

一、定義用戶組1.1 simpleAuthenticationPlugin通過在activemq.xml中配置用戶組<plugins> <simpleAuthenticationPlugin> <users> <authenticationUser username"admin" password"password" groups"admins,publishers,consumer…

React Native在Android當中實踐(五)——常見問題

React Native在Android當中實踐&#xff08;一&#xff09;——背景介紹 React Native在Android當中實踐&#xff08;二&#xff09;——搭建開發環境 React Native在Android當中實踐&#xff08;三&#xff09;——集成到Android項目當中 React Native在Android當中實踐&#…

完成登錄與注冊頁面的前端

完成登錄與注冊頁面的HTMLCSSJS&#xff0c;其中的輸入項檢查包括&#xff1a; 用戶名6-12位 首字母不能是數字 只能包含字母和數字 密碼6-12位 注冊頁兩次密碼是否一致 JS&#xff1a; function fnLogin() {var uSer document.getElementById("user");var pAss do…

mysql505復位密碼_mysql5 如何復位根用戶密碼[官方文檔]

如何復位根用戶密碼如果你從未為MySQL設置根用戶密碼&#xff0c;服務器在以根用戶身份進行連接時不需要密碼。但是&#xff0c;建議你為每個賬戶設置密碼如果你以前設置了根用戶密碼&#xff0c;但卻忘記了該密碼&#xff0c;可設置新的密碼。下述步驟是針對Windows平臺的。在…

WPF效果第二百零一篇之實現合并單元格

早一段時間又一次出差青海省西寧市;回來又是總結又是各種瑣事,也沒顧得上去分享點東西;大周末的就在家分享一下,這二天再次基于ListBox實現的合并單元格的效果:1、ListBox嵌套ListBox的前臺布局:<ListBox ItemsSource"{Binding LCPListData}" x:Name"Manufac…

轉載 maven 詳解 http://www.cnblogs.com/binyue/p/4729134.html

--聲明規范 <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <!--聲…

ASP.NET Core中使用EasyCaching作為緩存抽象層

簡介做后端開發&#xff0c;緩存應該是天天在用&#xff0c;很多時候我們的做法是寫個幫助類&#xff0c;然后用到的時候調用一下。這種只適合簡單層次的應用&#xff1b;一旦涉及到接口實現調整之類的&#xff0c;這種強耦合的做法很不合適。有些其他的功能又要去重復造輪子。…

mysql qps如何查看_mysql狀態查看 QPS/TPS/緩存命中率查看

運行中的mysql狀態查看對正在運行的mysql進行監控&#xff0c;其中一個方式就是查看mysql運行狀態。(1)QPS(每秒Query量)QPS Questions(or Queries) / uptimemysql > show global status like Question%;mysql > show global status like uptime%;(2)TPS(每秒事務量…

visual studio開啟多核編譯方法

先按http://blog.csdn.net/acaiwlj/article/details/50240625的方法進行了VS多線程的啟動。 原本以為按以下步驟設置就OK了&#xff0c;但是編譯中無意間發些了一個warning&#xff1a;“/Gm”與多處理不兼容&#xff1b;忽略 /MP 開關&#xff01;&#xff01;&#xff01;&am…