React 核心概念(3)
1.事件處理
React 元素的事件處理和 DOM 元素的很相似,但是有一點語法上的不同:(引自: 事件處理 )
React 事件的命名采用小駝峰式(camelCase),而不是純小寫,
使用 JSX 語法時你需要傳入一個函式作為事件處理函式,而不是一個字串,
1.1 系結事件
還記得在之前文章中我們寫過的增加計數的 class 組件嗎?我們這里會再一次用到:
import React from 'react';
class ClassComp extends React.Component {
constructor (props) {
super(props);
this.state = {
count: 0,
total: 100,
}
}
increase = () => {
const { count } = this.state;
this.setState({ count: count + 1 });
}
render () {
const { count } = this.state;
return (
<>
<div>{count}</div>
<button onClick={this.increase}>點擊</button>
</>
)
}
}
export default ClassComp;
可以看到,在 increase 函式中,我們寫了這樣一句:
const { count } = this.state;
這里的 this 是什么呢,可以列印出來看一下:
increase = () => {
console.log(this);
const { count } = this.state;
this.setState({ count: count + 1 });
}

可以看到,this 列印的結果是一個物件,里面的確有 state 這個屬性,請先記住這個結果,我們再來看一下這種寫法:
increase() {
console.log(this);
const { count } = this.state;
this.setState({ count: count + 1 });
}
列印結果如下:

頁面也是直接報錯:

只是換了函式的一種寫法,怎么會出現這種情況呢?
其實這就是一開始講到的,系結事件,
在 JavaScript 中,class 的方法默認不會系結 this,如果你忘記系結 this.increase 并把它傳入了 onClick,當你呼叫這個函式的時候 this 的值為 undefined,
這并不是 React 特有的行為;這其實與 JavaScript 函式作業原理 有關,通常情況下,如果你沒有在方法后面添加 (),例如 onClick={this.handleClick},你應該為這個方法系結 this,(引自:事件處理 )
解決此問題的辦法有三種:
方法一:一開始我們使用的箭頭函式,此語法確保函式內的 this 已被系結,
方法二:
<button onClick={() => this.increase()}>點擊</button>
但實際上并不推薦使用改方法,因為這樣書寫,每次組件重新渲染時都是創建不同的回呼函式,
方法三:
在 constructor 中通過 bind 函式進行提前系結,簡單示例如下:
this.increase = this.increase.bind(this);
1.2 react事件中的 event 引數
首先來看一段代碼:
import React from 'react';
class ReactEvent extends React.Component {
handleClick1 = (event) => {
console.log(this)
console.log(event)
}
handleClick2(event) {
console.log(this)
console.log(event)
}
render() {
return (
<>
<button onClick={this.handleClick1}>handleClick1</button>
<button onClick={this.handleClick2.bind(this)}>handleClick2</button>
</>
)
}
}
export default ReactEvent;
可以看見,在 button 上的 onClick 中并沒有傳入引數,但是卻能在函式中列印出來 event,這是因為無論在箭頭函式還是普通函式中,當我們不傳任何引數時,函式都會默認追加了一個event引數,
當為 handleClick 傳入多個引數時:
1.箭頭函式傳入多個引數需要手動傳 event 進去
2.bind系結方式從第二個引數開始傳的是引數值,可以不傳 event 引數,會自動追加在最后一個引數,
import React from 'react';
class ReactEvent extends React.Component {
handleClick1 = (id, name, event) => {
console.log(this)
console.log(id, name, event)
}
handleClick2(id, name, event) {
console.log(this)
console.log(id, name, event)
}
render() {
return (
<>
<button onClick={(e) => { this.handleClick1(1, 'handleClick1', e) }}>handleClick1</button>
<button onClick={this.handleClick2.bind(this, 2, 'handleClick2')}>handleClick2</button>
</>
)
}
}
export default ReactEvent;
注意,上面的代碼是以 this 不會丟失為前提的,
1.3 關于 react 中的 event 引數
這里的 event 是 React 封裝的,是一個合成事件,而并非原生的 event ,可以看 __ proto __.constructor 的 name 是 SyntheticBaseEvent(組合基礎事件),

那么原生的 event 怎么拿到呢,可以通過 nativeEvent 來獲取:
console.log('nativeEvent', event.nativeEvent)
在 react 的17版本以前,所有的事件都是被掛載到 document 上,而到了17版本,修改為掛載到root節點上,

1.4 target 和 currentTarget 的區別
1.DOM事件中的 target 和 currentTarget
target:事件觸發的真實元素
currentTarget :事件系結的元素
currentTarget和target,有時候是同一個元素,有時候不是同一個元素 (因為事件冒泡):
1.當事件是子元素觸發時,currentTarget為系結事件的元素,target為子元素
2.當事件是元素自身觸發時,currentTarget和target為同一個元素
更詳細介紹,可以參考: JS target 和 currentTarget 的區別
2.React中事件的 target 和 currentTarget
在 React 中實作了自己特有的一套組合事件,其中看似與 DOM 中保持一致的 currentTarget 本質上與DOM的并不一致,
在 React 中的事件實際上是系結在了 document 上的,(版本17修改為系結到root節點上),通過 react 的事件處理機制后根據 target 進行派發,
然而為什么 React 使用者在使用程序中感受不到區別呢,看下面一段代碼:
handleClick = (event) => {
console.log(event) // react 組合的 event
console.log(event.target) // 事件觸發的真實元素
console.log(event.currentTarget) // 事件系結的元素
console.log(event.nativeEvent.currentTarget) // 輸出:<div id="root">...</div>
}
當觸發該函式后,event.currentTarget 和 DOM事件中表現一致,這其實是一種假象,這是react為了不讓用戶感覺到做的處理而已,而從 event.nativeEvent.currentTarget 的輸出可以看出實際上是系結到 root 上的(版本17以前是系結到 document 上),
1.5 阻止冒泡和組織默認操作
event.stopPropagation() 方法 這是阻止事件的冒泡方法,不讓事件向documen上蔓延,但是默認事件任然會執行,當你掉用這個方法的時候,如果點擊一個連接,這個連接仍然會被打開,
event.preventDefault() 方法 這是阻止默認事件的方法,呼叫此方法是,連接不會被打開,但是會發生冒泡,冒泡會傳遞到上一層的父元素;
return false ,會同時阻止事件冒泡也會阻止默認事件,寫上此代碼,連接不會被打開,事件也不會傳遞到上一層的父元素;可以理解為return false就等于同時呼叫了event.stopPropagation()和event.preventDefault() (**在 React 中不能使用回傳 false 的方式阻止默認行為, 你必須明確用 preventDefault **,)
合成事件參考: 合成事件
2.判斷條件
2.1 if / else
render() {
const num = <p style={{fontSize: '30px', color: 'red'}}>num</p>
const str = <p style={{fontSize: '30px', color: 'blue'}}>str</p>
const isNumOrStr = 'num'
if(isNumOrStr === 'num') {
return num
} else {
return str
}
}
2.2 三元運算子
render() {
const num = <p style={{fontSize: '30px', color: 'red'}}>num</p>
const str = <p style={{fontSize: '30px', color: 'blue'}}>str</p>
const isNumOrStr = 'num'
return (
<>
{isNumOrStr === 'num' ? num : str}
</>
)
}
2.3 &&
render() {
const num = <p style={{fontSize: '30px', color: 'red'}}>num</p>
const str = <p style={{fontSize: '30px', color: 'blue'}}>str</p>
const isNumOrStr = 'num'
return (
<>
{isNumOrStr === 'num' && num}
{isNumOrStr === 'str' && str}
</>
)
}
2.4 阻止組件渲染
在極少數情況下,你可能希望能隱藏組件,即使它已經被其他組件渲染,若要完成此操作,你可以讓 render 方法直接回傳 null,而不進行任何渲染,
注:在組件的 render 方法中回傳 null 并不會影響組件的生命周期,例如,上面這個示例中,componentDidUpdate 依然會被呼叫,
3.渲染串列
3.1 簡單示例
render() {
const list = [
{ key: 'list1', val: 1 },
{ key: 'list2', val: 2 },
{ key: 'list3', val: 3 },
]
return (
<>
<ul>
{
list.map((item, index) => {
return <li key={item.key}>index: {index}, item: {item.val}</li>
})
}
</ul>
</>
)
}
可以通過 map 這個方法進行遍歷陣列,從而渲染串列
3.2 Key
key 幫助 React 識別哪些元素改變了,比如被添加或洗掉,因此你應當給陣列中的每一個元素賦予一個確定的標識,
一個元素的 key 最好是這個元素在串列中擁有的一個獨一無二的字串,但是在無法獲取一個獨一無二的字串時,可以用元素索引 index 作為 key,不過這樣會有弊端:
如果串列專案的順序可能會變化,不建議使用索引來用作 key 值,因為這樣做會導致性能變差,還可能引起組件狀態的問題,如果你選擇不指定顯式的 key 值,那么 React 將默認使用索參考作為串列專案的 key 值,
關于Key的相關內容,可以直接查閱官方檔案: React Key
深度決議使用索引作為 Key: 深度決議使用索引作為 Key
注:在講到到虛擬 DOM 與 Diff 演算法的內容時,還會再一次提起 key,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/297592.html
標籤:其他
