React
中的props
是一种用于组件之间传递数据的机制。它是一个对象,包含了组件的属性和值。当一个组件被渲染时,它的props
对象会被传递给它。这样,组件就可以使用这些属性来渲染自己。
案例分析
以下案例是展示一个人的信息。
<!-- 准备好员工“容器” --> <div id="app"></div> <!-- 引入ReactJS核心库 --> <script type="text/javascript" src="../JS/react.development.js"></script> <!-- 引入React-DOM核心库,用于操作DOM --> <script type="text/javascript" src="../JS/react-dom.development.js"></script> <!-- 引入Babel,用于编译jsx为js --> <script type="text/javascript" src="../JS/babel.min.js"></script> <!-- 此处类型为babel --> <script type="text/babel"> class Person extends React.Component { state = {name:"Tom",sex:"man",age:"20"} render () { return ( <ul> <li>name:{this.state.name}</li> <li>sex:{this.state.sex}</li> <li>age:{this.state.age}</li> </ul> ) } } // 2、将虚拟DOM渲染到页面,标签必须闭合 ReactDOM.render(<Person/>,document.getElementById('app')) </script>
此时页面展示的内容:
- name:Tom
- sex:man
- age:20
以上组件的内容信息是使用状态(state)完成的,但是这样会出现一个问题:每个人的信息应该是不一样的,但是通过以上组件生成出来的人都是一模一样的的。
ReactDOM.render(<Person/>,document.getElementById('app1')) ReactDOM.render(<Person/>,document.getElementById('app2')) ReactDOM.render(<Person/>,document.getElementById('app3'))
页面内容:
- name:Tom
- sex:man
- age:20
- name:Tom
- sex:man
- age:20
- name:Tom
- sex:man
- age:20
我们渲染了三个人的组件信息,发现都是一模一样的,且不能改变。这是非常不合理的,所以我们要使用一个新的属性props
来实现我们需要的功能。
使用props属性改造案例
我们参考官网,使用props
改造以上案例。
class Person extends React.Component { render () { const {name,age,sex} = this.props return ( <ul> <li>name:{name}</li> <li>sex:{sex}</li> <li>age:{age}</li> </ul> ) } } // 2、将虚拟DOM渲染到页面,标签必须闭合 ReactDOM.render(<Person name="genius" age="25" sex="男"/>,document.getElementById('app'))
页面内容
- name:genius
- sex:男
- age:25
以上就是我们使用props
改造的案例,我们发现它和HTML
标签属性的使用方式差不多,你传什么属性react
就把你的属性放在props
对象身上,组件内部使用的时候,就从props
身上拿出来使用。
这个时候我们就可以通过多个组件实例生成不同的人的信息了
ReactDOM.render(<Person name="Tom" age="20" sex="男"/>,document.getElementById('app1')) ReactDOM.render(<Person name="李琴" age="25" sex="女"/>,document.getElementById('app2')) ReactDOM.render(<Person name="张三" age="25" sex="男"/>,document.getElementById('app3'))
以上我们了解了props
的基础使用方式。
批量传递标签属性
以上案例我们使用的属性很少,但是在真实项目中,我们使用网络请求从数据库拿到的信息非常多,难道我们也要一个一个的往组件身上加属性吗?react
为我们提供了一个便捷的方式。
const p = {name:"Tom",sex:"man",age: 20} ReactDOM.render(<Person {...p} />,document.getElementById('app'))
我们可以使用扩展运算符将属性批量的添加到组件里面去,效果是一样的。
注意:需要我们注意的是你传入的属性名是什么,在组件内部使用的时候必须一致,否则是个空值。
控制props的数据类型与默认值
现在我们加一个需求,让组件信息里面的年龄在真实年龄的基础上+1,并展示。
class Person extends React.Component { render () { const {name,age,sex} = this.props return ( <ul> <li>name:{name}</li> <li>sex:{sex}</li> <li>age:{age + 1}</li> </ul> ) } } // 2、将虚拟DOM渲染到页面,标签必须闭合 ReactDOM.render(<Person name="Tom" age="25" sex="男"/>,document.getElementById('app'))
页面展示:
- name:Tom
- sex:男
- age:251
我们可以看到,我们的年龄变成了251,说明该运算进行的是【字符串连接】而不是【数值运算】。说明是我们的props
属性类型出了问题,我们修改一下
ReactDOM.render(<Person name="Tom" age={25} sex="男"/>,document.getElementById('app'))
我们在修改代码以后,在看结果:
- name:Tom
- sex:男
- age:26
修改了标签属性的数据类型后,年龄运算进行了【数值运算】。
但是这是我们知道该组件该传入什么数据类型的情况下进行的修改,但是别人不一定知道,有什么方法提示别人传入标签属性的数据类型呢?如果不传或者漏传一个属性,其默认值该如何设置呢?
步骤1:引入prop-types
用于组件的属性类型限制
<!-- 引入prop-types 用于组件的属性类型限制 --> <script src="../JS/prop-types.js"></script>
引入了该脚本后,全局多了一个叫PropTypes
的对象。
步骤2:给组件的标签属性设置类型限制
// 设置属性类型以及是否必填 Person.propTypes = { name: PropTypes.string.isRequired, // 限制字符串,且必传 sex: PropTypes.string, // 限制字符串 age: PropTypes.number, // 限制数值 speak: PropTypes.func // 限制函数 }
通过给组件类一个特定的属性propTypes
来规范标签属性的数据类型限制。
步骤3:给组件的标签属性设置默认值
// 对属性值设置默认值 Person.defaultProps = { sex: '不男不女', age: 18 }
通过给组件类一个特定的属性defaultProps
来设置标签属性的默认值,当没有传入该属性值时显示其默认值。
步骤4:测试是否有效
const p = {name:9527, sex:'man', age:20} ReactDOM.render(<Person {...p} />,document.getElementById('app'))
以上配置会报错:
Failed prop type: Invalid prop `name` of type `number` supplied to `Person`, expected `string`.
错误说明:提供给“Person”的“number”类型的无效道具“name”,预期为“string”。
说明我们的类型限制生效了。当我们将name
属性的值改回字符串形式,错误就会消失。
在测试一下默认值是否生效:
const p = {name:'Tom'} ReactDOM.render(<Person {...p} />,document.getElementById('app'))
此时我们不传sex
和 age
属性,看看页面内容。
- name:Tom
- sex:不男不女
- age:19
说明sex
属性展示的是默认值,age
为什么是19
呢?因为我们在内部+1
了,所以age
也是使用的默认值18
。
到此说明我们的类型限制和默认值设置都已经生效了。
简化props的写法
之前我们使用propTypes
和defaultProps
都是通过类调用的,说明这是类的静态属性,我们以此修改代码:
class Person extends React.Component { // 设置属性类型以及是否必填 static propTypes = { name: PropTypes.string.isRequired, // 限制字符串,且必传 sex: PropTypes.string, // 限制字符串,默认值:不男不女 age: PropTypes.number, // 限制数值,默认值:18 speak: PropTypes.func // 限制函数 } // 对属性值设置默认值 static defaultProps = { sex: '不男不女', age: 18 } render () { console.log(this) const {name,age,sex} = this.props return ( <ul> <li>name:{name}</li> <li>sex:{sex}</li> <li>age:{age + 1}</li> </ul> ) } } // 2、将虚拟DOM渲染到页面,标签必须闭合 ReactDOM.render(<Person name="Tom" />,document.getElementById('app'))
扩展构造函数与props
在react官网中对构造函数是这样描述的:
如果不初始化 state 或不进行方法绑定,则不需要为 React 组件实现构造函数。
在 React 组件挂载之前,会调用它的构造函数。在为 React.Component 子类实现构造函数时,应在其他语句之前前调用 super(props)。否则,this.props 在构造函数中可能会出现未定义的 bug。
通常,在 React 中,构造函数仅用于以下两种情况:
在 constructor() 函数中不要调用 setState() 方法。如果你的组件需要使用内部 state,请直接在构造函数中为 this.state 赋值初始 state:
constructor(props) { super(props); // 不要在这里调用 this.setState() this.state = { counter: 0 }; this.handleClick = this.handleClick.bind(this); }
大概意思是如果你创建了构造函数,如果不调用super(props)
语句,在构造函数中的this.props
出出现未定义的bug。
我们尝试一下:
constructor(props){ super() console.log('constructor',this.props) }
这里我们打印的this.props
的结果是:undefined
。
将类式组件改造为函数式组件
在函数组件中,props是作为函数的参数传递的。所以我们的函数式可以使用props
。
function Person (props) { const {name,age,sex} = props return ( <ul> <li>name:{name}</li> <li>sex:{sex}</li> <li>age:{age + 1}</li> </ul> ); } // 设置属性类型以及是否必填 Person.propTypes = { name: PropTypes.string.isRequired, // 限制字符串,且必传 sex: PropTypes.string, // 限制字符串,默认值:不男不女 age: PropTypes.number, // 限制数值,默认值:18 speak: PropTypes.func // 限制函数 } // 对属性值设置默认值 Person.defaultProps = { sex: '不男不女', age: 18 } // 2、将虚拟DOM渲染到页面,标签必须闭合 ReactDOM.render(<Person name="Tom" />,document.getElementById('app'))
将其改为函数式组件,运行控制台不报错,且数据类型和默认值生效。
总结Props
1、props是一种用于组件之间传递数据的机制。它是一个对象,包含了组件的属性和值。
2、可以通过扩展运算符({…p})来批量传入标签属性
3、可以通过propTypes属性来限制标签属性的数据类型
4、可以通过defaultProps属性来设置标签属性的默认值
5、标签属性不能修改,只能读取
6、函数式组件也能使用props