上节课的内容我们正式开始学习了react组件三大属性的第二个属性props的基本使用。在上节课中或许大家还有疑问,比如如果我要传入的属性太多了在标签那边怎么写啊?这个问题我们这节课会给大家解释,没错就是props的批量传递

回顾

在正式学习这节课的内容之前,我们先来对上节课的内容做一个简单的回顾:

  • props是一个对象
  • props在组件的实例对象上,可以通过this调用
  • props是通过组件标签传入的
  • props是通过组件外向组件内传递数据

以上就是上节课的一写知识点,接下来我们来进行这节课内容的学习

批量传递props

我们在前文也提到过了,如果我们要传递的属性太多了,在组件标签那边一个属性一个属性地写,也太慢了。而且如果在实际的开发过程中,我们获取到的数据那是后端接口返回给我们的,那我们来模拟一下这种场景:

class Person extends React.Component {
  render() {
    const { name, age, gender } = this.props;
    return (
      <ul>
        <li>姓名:{name}</li>
        <li>性别:{gender}</li>
        <li>年龄:{age}</li>
      </ul>
    );
  }
}
const p = {name: "jingxun", age: 18, gender: ""};
ReactDOM.render(<Person name={p.name} age={p.age} gender={p.gender} />,...);

我们就需要想上面那样来写我们的渲染标签属性这样虽然可以,但是是不是如果信息一多,就会很繁琐。所以我们肯定是希望能够批量的把信息直接传入进去。

其实react是有提供一种方法的就是像下面这样来处理:

class Person extends React.Component {
  render() {
    const { name, age, gender } = this.props;
    return (
      <ul>
        <li>姓名:{name}</li>
        <li>性别:{gender}</li>
        <li>年龄:{age}</li>
      </ul>
    );
  }
}
const p = {name: "jingxun", age: 18, gender: ""};
ReactDOM.render(<Person {...p} />,...);

可能有些朋友要说了,这个我看不懂。这是什么意思呢,这其实就是一个语法糖,这种写法和上一种写法是一个意思。所以说这样我们就可以完成一个批量传递props

d但是有一个问题要注意:

我们在render方法中解构了this.props赋值给了name,agegender,那么必须得在我们这个p中的属性名称和render方法中的变量名称一致才可以,否则拿到的数据是有问题的,比如说:

class Person extends React.Component {
  render() {
    const { name, age, gender } = this.props;
    return (
      <ul>
        <li>姓名:{name}</li>
        <li>性别:{gender}</li>
        <li>年龄:{age}</li>
      </ul>
    );
  }
}
const p = {nm: "jingxun", age: 18, gender: ""};
ReactDOM.render(<Person {...p} />,...);

我们把p中的name改成了nm再来看一下页面渲染:

image-20211220201203147

通过图片上可以看出我们的这个姓名是没有拿到的,所以说必须要我们获取到的数据和调用时解构的变量保持一致才能正常使用。

展开运算符

可能前面的{...p}有朋友说这个东西是什么?看不明白,那么我们在这里简单说一下。

这个...是什么呢?这个东西叫做展开运算符,从我们上面的代码我们仿佛觉得可以通过...将一个对象展开,但是其实不是这样,展开运算符并不能展开对象,我们来回顾一下曾经学习的有关...的一些知识

let arr1 = [1, 3, 5, 7, 9];
let arr2 = [2, 4, 6, 8, 10];
console.log(arr1);
console.log(...arr1);
let arr3 = [...arr1, ...arr2];
console.log(arr3);

我们上面这段代码是做了写上面呢?我们定义了两个数组arr1arr2,然后我们在控制台分别打印arr1...arr1。这就是我们展开运算符的第一个用处,就是展开数组。接着往下看,我们定义了一个新数组arr3,这个数组将arr1arr2都展开然后放在一个新数组中,这就是展开运算符的第二个用法,就是连接两个数组。

好,那我们来看一下效果:

image-20211220202852152

第一行直接打印arr1第二行展开arr1之后再打印,第三行打印出将arr1arr2连接后的新数组。

那么除了这两种用法之外还有没有其他的用法呢?当然有在函数传参时使用,比如我这段代码:

function sum(a, b) { return a + b; }
console.log(sum(1, 2));

这段代码很简单就是求两个数的和,但是如果我现在要计算一组数的和,但是我不知道这组数有几个数字那么我们就可以使用展开运算符:

function sum(...numbers) { console.log(numbers); }
sum(1, 2);

我们来打印看一下这个numbers是个什么东西:

image-20211220203730185

我们可以看出来这是个数组,不管我们传多少数字进去,得到的都是一个数组,这样的话我们就可以使用一个函数来计算不确定个数的数字的和,这样的话免去了我们写多个函数的麻烦,那么我们这个函数体怎么写?看看大家对过去js语法的遗忘程度。

当然,我们可以用最基本的for循环来写,但是数组是不是有个方法叫reduce,我们直接用reduce怎么写呢?

function sum(...numbers) {
  return numbers.reduce((preValue, value) => {
    return preValue + value;
  });
}

如果大家对这个方法的使用都已经记不清了,那么大家抓紧回去复习一下数组上的方法。以上这一种就是配合函数传参时展开运算符的使用方法。

那么还有吗?我们来看一下在最开始我们就提到的,展开运算符能不能展开一个对象

let person = {name: "jingxun", age:18, gender: "男"};
console.log(...person);

这段代码再简单不过了,但是我们看看能不能行:

image-20211220204920539

诶,报错了,报错信息说对象类型没有iterator接口,也就是说不能直接把展开运算符直接运用到一个对象上,也就是说展开运算符不能展开一个对象。

有人要说了,你等会儿,刚才我们的组件标签传props的时候好像可以的啊。

别急我们等会儿再说这个问题,我们先思考另一个问题。如果我们想复制一个person对象怎么写?可以let person2 = person这么写吗?那我们试一下,我们在控制台分别打印personperson2看一下:

image-20211220205602466

看着好像没有问题,但是如果我们在打印person2之前做一件事儿,什么事儿呢:

let person = { name: "jingxun", age: 18, gender: "男" };
let person2 = person;
person.name = "dschow";
console.log(person);
console.log(person2);

我们把person中的name属性给改了,那么是什么效果呢:

image-20211220205825386

personperson2两个完全一样,我改的是person中的name属性,但是person2也跟着一起改了。这个根本不是复制,或许大家记得深拷贝和浅拷贝这两种概念,上面这种现象实际上就是一个引用关系的传递,也就是我们所谓的浅拷贝。

但是我们要想正常地去复制一个对象那么应该怎么办呢?其实我们有个便捷的方法,就是使用展开运算符。

let person = { name: "jingxun", age: 18, gender: "男" };
let person2 = {...person};
person.name = "dschow";
console.log(person);
console.log(person2);

我们再来看一下效果:

image-20211220210327475

这样就正常了,但是有人又说了,不是不能展开对象嘛,这么又用展开运算符了呢?

其实展开运算符不能直接对对象进行展开,我上面也说了是不能直接对对象使用,但是让我们在对象的{}内部的话是可以使用展开运算符来复制一个对象的,注意我这里说的是复制而不是展开对象。

所以说直接使用运算符作用在对象上是一个语法错误,如果在对象的{}内部用展开运算符展开作用在对象上则触发了一个新的语法,是复制一个对象。

然后有人又要说了,哦那我就明白了,之前<Person {...p} />这里不是展开一个对象,而是复制了一个对象。所以这里没有报错。但是事实并不是这样。这个组件标签里面的{}是什么意思?这是不是要引入js表达式啊?这并不是一个对象啊。实际上里面的js表达式其实就是...p,这时候有人要说了,我晕了,刚想好一个解释的理由直接给推翻了,又回到原来的问题来了,这里为啥可以展开对象呢?

其实没有大家想象的那么魔幻,我们这写的是什么啊?我们渲染组件写组件标签这不是jsx语法嘛,而且还要通过babel库来转换的,我们太久没有提这个,大家是不是都忘了有这么一回事了?其实原因就是这么简单,因为babel库和react的支持,使得我们可以在jsx语法环境下来用展开运算符来展开对象。但是在原生js语法中绝对是不允许展开对象的。

那么我们或许有些好奇,那么我们展开运算符展开一个对象打印出来会是什么样子啊?那么我们看一下呗:

const p = { nm: "jingxun", age: 18, gender: "男" };
console.log("...p:",...p);
ReactDOM.render(<Person {...p}/>, document.getElementById("test"));

我们来看一下效果:

image-20211220211926263

我们好像什么都没有打印出来啊,这是什么情况呢?

因为babel库虽然支持展开对象,但是并不允许我们随便使用,仅仅只在标签属性传递时才允许展开一个对象,别的地方都不行。

以上就是本节课的知识点

总结

  • props可以在标签中使用展开运算符展开一个对象来批量传入属性
  • props在使用展开运算符时必须确保被展开对象中的属性和解构props时的变量一致才能正常取数
  • 展开运算符在原生js语法中不能展开对象
  • babelreact库支持仅在组件标签批量传入属性时展开对象

Copyright statement:The articles of this site are all original if there is no special explanation, indicate the source please when you reprint.

Link of this article:https://work.lynchow.com/article/bat_pass_props/