React 核心概念
Posted 花轻言
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了React 核心概念相关的知识,希望对你有一定的参考价值。
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。
以上是关于React 核心概念的主要内容,如果未能解决你的问题,请参考以下文章