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 核心概念的主要内容,如果未能解决你的问题,请参考以下文章

React 核心概念

谈谈 React.js 的核心入门知识

React核心概念与JSX

React核心概念-第二篇

react中的核心概念

16.)React核心概念-DOM和虚拟DOM的概念