React中组件的props属性

什么是Props

props是组件(函数组件和class组件)间的内置属性,用于在组件之间传递数据。它是父组件向子组件传递信息的一种方式,通过 props,父组件可以向子组件传递数据、函数、配置项等。

初次使用Props

先定义一个父组件

import React from 'react';
import ReactDOM from 'react-dom/client';
import Child from './child'
const root = ReactDOM.createRoot(document.getElementById('root'));
// 父组件
function App(){
  return <Child title='个人信息' name="小浩" sex="男" age="30"></Child>
}
root.render(
  <>
    <App></App>
  </> 
);


在父组件中的Child上面添加属性和属性的值,这样父组件就把信息传递给了子组件。那么子组件要怎么使用呢?
创建子组件

import React from 'react';
class Child extends React.Component{
 render(){
  console.log(this.props)//输出props信息
  return  <div className="child">
   <h2>{this.props.title}</h2>
   <ul>
    <li>姓名:{this.props.name}</li>
    <li>性别:{this.props.sex}</li>
    <li>年龄:{this.props.age}</li>
   </ul>
  </div>
 }
}
export default Child

可以看到子组件直接使用this.props就可以拿到父组件传的信息了。
在浏览器渲染的name、sex、age属性和在控制台中可以看到在子组件中打印的props对象,可以清晰看到在父组件的Child组件上添加的属性信息。
浏览器中输出的props对象

上面我们就只传递了几个参数,直接在Child上写还是比较方便的,那如果需要传递的属性特别多还要一个个添加吗?那如何批量传递呢?
批量传递Props
我们可以利用Js的展开运算符来实现批量传递参数,看下面这段代码:

//父组件
import React from 'react';
import ReactDOM from 'react-dom/client';
import Child from './child'
const root = ReactDOM.createRoot(document.getElementById('root'));
function App(){
  // 定义要传递的用户信息对象
  const user = {
    title:'个人信息',
    name:'小浩',
    sex:'男男',
    age:30,
    work:'程序员'
  }
  return <Child {...user}></Child>
}
root.render(
  <>
    <App></App>
  </> 
);

这里我创建了user这个变量对象:

const user = {
    title:'个人信息',
    name:'小浩',
    sex:'男男',
    age:30,
    work:'程序员'
  }

然后使用{...user}就可以一次性批量传入user里面的属性数据,简单快捷。

// 子组件
import React from 'react';
class Child extends React.Component{
 render(){
  console.log(this.props)
  return  <div className="child">
   <h2>{this.props.title}</h2>
   <ul>
    <li>姓名:{this.props.name}</li>
    <li>性别:{this.props.sex}</li>
    <li>年龄:{this.props.age}</li>
    <li>工作:{this.props.work}</li>
   </ul>
  </div>
 }
}
export default Child

浏览器输出
浏览器中输出的props对象

类组件中使用Props

在上面初次使用Props中,可以看出在类组件,是通过this.props拿到父组件传递的对象信息。
那如果想要年龄增加1,要怎么做呢?是直接+1吗?下面来看看效果:

父组件中使用属性传递数据

function App(){
  return <Child title='个人信息' name="小浩" sex="男" age="30" work="程序员"></Child>
}

子组件中接收传递的数据this.props.age + 1

import React from 'react';
class Child extends React.Component{
 render(){
  console.log(this.props)
  return  <div className="child">
   <h2>{this.props.title}</h2>
   <ul>
    <li>姓名:{this.props.name}</li>
    <li>性别:{this.props.sex}</li>
    <li>年龄:{this.props.age + 1}</li>
    <li>工作:{this.props.work}</li>
   </ul>
  </div>
 }
}
export default Child

浏览器输出
浏览器中输出的props对象
可以看到年龄变成了301,变成字符串拼接了。那要如何传递呢?

我们只需要在父组件中传递的年龄加上花括号就会转为number类型。

function App(){
  return <Child title='个人信息' name="小浩" sex="男" age={30} work="程序员"></Child>
}

浏览器输出
浏览器中输出的props对象

既然props是个对象,那我们可以直接修改props里面的属性值和增加属性吗?
下面试着修改props里的work属性值为销售

class Child extends React.Component{
 render(){
  console.log(this.props)
  // 在这里试着修改props里的work属性
  this.props.work = '销售'
  return  <div className="child">
   <h2>{this.props.title}</h2>
   <ul>
    <li>姓名:{this.props.name}</li>
    <li>性别:{this.props.sex}</li>
    <li>年龄:{this.props.age}</li>
    <li>工作:{this.props.work}</li>
   </ul>
  </div>
 }
}

上面修改了propswork的值,浏览器就提示错误:
浏览器中输出的props对象
上面报错提示信息就是告诉你:

props属性是只读的,不允许修改

那么为什么不能修改props的属性呢?

扫盲知识点:关于对象的规则设置

  • 冻结
    冻结对象: Object.freeze(obj)
    检测是否被冻结: Object.isFrozen(obj) =>true/false
    被冻结的对象:不能修改成员值、不能新增成员、不能删除现有成员、不能给成员做劫持 Object,defineProperty
  • 密封
    密封对象: Object.seal(obj)
    检测是否被密封: Object.isSealed(obj)
    被密封的对象: 可以修改成员的值,但也不能删、不能新增、不能劫持!!
  • 扩展
    把对象设置为不可扩展: Object.preventExtensions(obj)
    检测是否可扩展: Object.isExtensible(obj)
    被设置不可扩展的对象: 除了不能新增成员、其余的操作都可以处理! !

总结:被冻结的对象,是不可扩展的,也是密封的!!同理,被密封的对象,也是不可扩展的!!
下面再来看看子组件Childthis.props是怎样的对象规则:

class Child extends React.Component{
 render(){
  console.log(Object.isFrozen(this.props)) //  true
  console.log(Object.isSealed(this.props))  //  true
  console.log(Object.isExtensible(this.props)) //  false
  
  return  <div className="child">
   <h2>{this.props.title}</h2>
   
  </div>
 }
}

至此,了解到为什么不能修改props的属性了,因为props对象是被冻结的、密封的且不可扩展的。

函数组件中使用Props

问题:函数式组件中没有this,那如何拿到props属性的值呢?
看下面这段代码:

import React from 'react';
function Child(props){
  console.log(props) // 打印props信息
  const {title, name, sex, age, work} = props
   return  <div className="child">
   <h2>{title}</h2>
   <ul>
    <li>姓名:{name}</li>
    <li>性别:{sex}</li>
    <li>年龄:{age}</li>
    <li>工作:{work}</li>
   </ul>
  </div>
}
export default Child

可以看到函数本身可以接收到一个props参数拿到父组件传递的信息。

Props设置默认值

类组件中设置默认值

  • 方式一:在子组件中使用 defaultProps 设置默认值
    父组件就可以不用添加work属性
function App(){
  return <Child title='个人信息' name="小浩" sex="男" age={30}></Child>
}

子组件中给Child添加defaultProps属性对象

class Child extends React.Component{
 render(){
  console.log(this.props)
  return  <div className="child">
   <h2>{this.props.title}</h2>
   <ul>
    <li>姓名:{this.props.name}</li>
    <li>性别:{this.props.sex}</li>
    <li>年龄:{this.props.age}</li>
    <li>工作:{this.props.c}</li>
   </ul>
  </div>
 }
}
// 设置默认值
Child.defaultProps ={
 work:'程序员'
}
export default Child
  • 方式二:在Child内添加静态属性 static defaultProps = {} 设置默认值
class Child extends React.Component{
  // 设置默认值
 static defaultProps = {
  work:'程序员'
 }
 render(){
  console.log(this.props)
  return  <div className="child">
   <h2>{this.props.title}</h2>
   <ul>
    <li>姓名:{this.props.name}</li>
    <li>性别:{this.props.sex}</li>
    <li>年龄:{this.props.age}</li>
    <li>工作:{this.props.work}</li>
   </ul>
  </div>
 }
}
export default Child

函数组件中设置默认值

  • 方式一:使用 defaultProps 设置默认值
function Child(props){
  const {title, name, sex, age, work} = props
   return  <div className="child">
   <h2>{title}</h2>
   <ul>
    <li>姓名:{name}</li>
    <li>性别:{sex}</li>
    <li>年龄:{age}</li>
    <li>工作:{work}</li>
   </ul>
  </div>
}
Child.defaultProps = {
  work:'程序员'
}
export default Child
  • 方式二:通过解构props时给属性设置默认值
function Child(props){
  console.log(props)
  const {title, name, sex, age, work = '程序员'} = props
   return  <div className="child">
   <h2>{title}</h2>
   <ul>
    <li>姓名:{name}</li>
    <li>性别:{sex}</li>
    <li>年龄:{age}</li>
    <li>工作:{work}</li>
   </ul>
  </div>
}
export default Child

Props验证

react V15.5之前,限制props的写法是:React.PropTypes

class Child extends React.Component { 

}

Child.propTypes = {
	age: React.PropTypes.number,   
}

react V15.5之后,限制props的写法是:1、需要引入prop-types的库 2、PropTypes
安装

npm install prop-types -D

引入

import PropTypes from 'prop-types' 

使用

import React from 'react';
import PropTypes from 'prop-types' 
class Child extends React.Component{
 static defaultProps = {
  work:'程序员'
 }
 render(){
  console.log(this.props)
  return  <div className="child">
   <h2>{this.props.title}</h2>
   <ul>
    <li>姓名:{this.props.name}</li>
    <li>性别:{this.props.sex}</li>
    <li>年龄:{this.props.age}</li>
    <li>工作:{this.props.work}</li>
   </ul>
  </div>
 }
}

Child.propTypes =  {
  name: PropTypes.string.isRequired,
  sex: PropTypes.string,
  age: PropTypes.number,
  work: PropTypes.string   
}
export default Child

可以看到上面age我们限制的是number类型,如果父组件age传过来的是string类型会怎样呢?

function App(){
  return <Child title='个人信息' name="小浩" sex="男" age='30'></Child>
}

浏览器输出
propTypes验证
浏览器控制台提示红色告警,告诉我们age传的是string类型,不支持,应该传number类型

function App(){
  return <Child title='个人信息' name="小浩" sex="男" age={30}></Child>
}

Props常见的约束规则

常用的类型限制

  PropTypes.array //数组
  PropTypes.bool  //布尔
  PropTypes.func  //函数
  PropTypes.object  //对象
  PropTypes.number  //数值
  PropTypes.string  //字符串
  PropTypes.symbol  //Symbol
  PropTypes.element  //React元素

必填项:isRequired—在类型后面添加

Child.propTypes = {
  //限制age为数字类型,且必需
	age: PropTypes.number.isRequired
}

仅限制必要任何类型:any.isRequired

Child.propTypes = {
  //限制age类型随意,但必需
	age: PropTypes.any.isRequired
}

多可选类型:oneOfType([])

Child.propTypes = {
  //该方法接受一个数组参数,数组内容为允许通过的类型
	age: PropTypes.oneOfType(
    	[PropType.string,PropType.number]
  )
}

多可选值:oneOf([])

Child.propTypes = {
  //sex的值只能从男和女中选择一个
	sex: PropTypes.oneOf(
    	['男', '女']
  )
}

限制对象结构:shape({})

Child.propTypes = {
  //user = {name: '小浩', id: 1} 满足校验
	user: PropTypes.shape({
    	name: PropTypes.string.isRequired,
    	id: PropTypes.number.isRequired
	})
}

下面我们来实践下,看下面这段代码:
父组件传递属性数据

function App(){
  return <Child title='个人信息' name="小浩" sex="男" age={30} info={{desc:'用户介绍',id:1}}></Child>
}

子组件做props限制并显示

import React from 'react';
import PropTypes from 'prop-types' 

class Child extends React.Component{
 static defaultProps = {
  work:'程序员'
 }
 render(){
  console.log(this.props)
  return  <div className="child">
   <h2>{this.props.title}</h2>
   <ul>
    <li>姓名:{this.props.name}</li>
    <li>性别:{this.props.sex}</li>
    <li>年龄:{this.props.age}</li>
    <li>工作:{this.props.work}</li>
    <li>介绍:{this.props.info.desc}</li>
   </ul>
  </div>
 }
}

Child.propTypes =  {
  name: PropTypes.string.isRequired,
  sex: PropTypes.oneOf(['男', '女']),
  age: PropTypes.number.isRequired,
  work: PropTypes.string,   
  info: PropTypes.shape({desc: PropTypes.string.isRequired,id: PropTypes.number.isRequired})
}
export default Child

总结:如果设置了props验证,就必须按照规定。


 上一篇
React中的插槽 React中的插槽
在开发中,我们抽取了一个组件,但是为了让这个组件具备更强的通用性,我们不能将组件中的内容限制死了。我们应该让使用者决定这一块区域应该显示什么内容。那么应该怎么做呢?这就需要使用所谓的插槽了,那什么是插槽呢? 什么是插槽 简单来说,插槽(sl
2022-04-07
下一篇 
React渲染原理 React渲染原理
渲染处理 在React中,是将编写的Jsx语法编译为虚拟DOM对象,然后将构建的虚拟DOM对象渲染为真实dom,最后将真实DOM渲染到页面中的过程。 第一次渲染页面是直接从虚拟DOM转化编译为真实DOM,后面每当状态发生改变时,React会
2022-04-06
  目录