一、props 的介绍
当 React 遇到的元素是用户自定义的组件,它会将 JSX 属性作为单个对象传递给该组件,这个对象称之为“props”。
函数声明的组件,会接受一个props形参,获取属性传递的参数
function ComponentA(props) {
return <div>我是组件B:{props.value}</div>
}
复制代码
如果函数组件需要 props 功能,一定不能缺少该形参类的声明,在 react 组建中,使用 constructor 获取 Component 类的 props 属性当组件继承了父类 props 后,就可以通过 this.props 属性名进行属性传值
class ComponentB extends React.Component {
constructor(props) {
super(props);
}
render() {
return <div>我是组件B {this.props.name}</div>
}
}
复制代码
类的继承子类必须在constructor方法中调用super方法,否则新建实例时会报错
。
这是因为子类自己的 this 对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。如果不调用 super 方法,子类就得不到 this 对象。
注意: props 可以传递任何数据类型,并且 props 是只读的(单项数据流),所有的 React 组件必须像纯函数那样使用它们的 props。
二、批量传递 props
情景: 有时我们要传递的参数不止一个的话,那如果是每个都写,10 个也许你能接受,那 100 个,1000 个呢。那你的代码简直神了。
既然如此,我们就借用 ES6 中的**展开运算符(...)**,就是三个点这玩意。
我们直接先定义好传递的参数,然后再传递。
class Person extends React.Component {
render() {
console.log(this); // Person 实例对象
const { name, age, sex } = this.props;
return (
<ul>
<li>姓名: {name}</li>
<li>性别: {sex}</li>
<li>年龄: {age}</li>
</ul>
)
}
}
// 单个传递
ReactDOM.render(<Person name="Tom" age="18" sex="woman" />, document.getElementById('test'))
ReactDOM.render(<Person name="Jack" age="19" sex="man" />, document.getElementById('test1'))
// 批量传递
const p = { name: '老王', age: 30, sex: 'man' }
ReactDOM.render(<Person {...p}/>, document.getElementById('test2'))
复制代码
三、props 的验证
随着应用日渐庞大,通常你希望每个 props 都有指定的值类型,并可以通过类型检查捕获大量错误,便捷开发减少异常维护时间,要检查组件的 props 属性,你需要配置组件特殊的静态 propTypes 属性并配合 prop-types 三方库实现 prop 验证。(prop-types 在 react 脚手架中自带无需下载)
在 16 版本之前的方式
ComponentA.propTypes = {
name: React.PropTypes.string.isRequired, // 限制name必传,且为字符串
}
复制代码
16 版本之后,单独作为一个库使用
写法一: 给类组件的 class 设置属性 propTypes
import React, {Component} from 'react'
import PropTypes from 'prop-types'
class ComponentA extends Component {
render() {
// 因为 jsx 元素本质上是 React.createElement() 隐式调用的
// 所以如果你的js文件中包含jsx元素就必须import React 支持让jsx元素隐式调用否则编译器会报错
// 'React' must be in scope when using JSX
return (
<div>
<p>name: {this.props.name}</p>
<p>age: {this.props.age}</p>
</div>
)
}
}
ComponentA.propTypes = {
name: PropTypes.string,
age: PropTypes.number
}
export default ComponentA
复制代码
参考 React 实战视频讲解:进入学习
使用 class 静态属性语法(static) 设置 propTypes,类的自身
添加的属性。
import React, {Component} from 'react'
import PropTypes from 'prop-types'
class ComponentA extends Component {
static propTypes = {
name: PropTypes.string,
age: PropTypes.number
}
render() {
// 因为 jsx 元素本质上是 React.createElement() 隐式调用的
// 所以如果你的js文件中包含jsx元素就必须import React 支持让jsx元素隐式调用否则编译器会报错
// 'React' must be in scope when using JSX
return (
<div>
<p>name: {this.props.name}</p>
<p>age: {this.props.age}</p>
</div>
)
}
}
export default ComponentA
复制代码
默认属性值,当某个属性没有传递的时候,就使用你定义的值
// 指定默认标签属性值
Person.defaultProps = {
sex: '男',
age: 17
}
复制代码
函数组件支持通过给构造函数设置属性,进行组件 props 验证
import React, {Component} from 'react'
import PropTypes from 'prop-types'
class ComponentA extends Component {
static propTypes = {
name: PropTypes.string,
age: PropTypes.number
}
render() {
// 因为 jsx 元素本质上是 React.createElement() 隐式调用的
// 所以如果你的js文件中包含jsx元素就必须import React 支持让jsx元素隐式调用否则编译器会报错
// 'React' must be in scope when using JSX
return (
<div>
<p>name: {this.props.name}</p>
<p>age: {this.props.age}</p>
</div>
)
}
}
export default ComponentA
复制代码
四、类式组件中的构造器与 props
如果不初始化 state 或不进行方法绑定,则不需要为 React 组件实现构造函数。
在 React 组件挂载之前,会调用它的构造函数。在为 React.Component 子类实现构造函数时,应在其他语句之前前调用 super(props)
。否则,this.props
在构造函数中可能会出现未定义的 bug。
通常,在 React 中,构造函数仅用于以下两种情况:
// state的基本使用
constructor(props) {
super(props);
// 初始化状态
this.state = {
isHot: true,
wind: '大风'
}
// bind: 做了两件事情 ---- 生成新的函数并且改变this为Weather的实例对象
// this.changeWeather是原型上的方法,通过bind改变this之后生成新的方法放在了实例自身上,导致了实例中也有changeWeather这个方法,这样就能进行调用了
this.changeWeather = this.changeWeather.bind(this);
}
复制代码
传不传 props 之间的区别
class Person extends React.Component {
// 1、传入props并且也传给了super
constructor(props) {
// console.log(props)
super(props);
console.log(this.props); // 组件所传入的所有props 如:{name: "Tom", sex: "男", age: 17, speak: ƒ}
}
// 2、传入props但不传给super
constructor(props) {
// console.log(props)
super();
console.log(this.props); // undefined
console.log(props); // 组件所传入的所有props 如:{name: "Tom", sex: "男", age: 17, speak: ƒ}
}
// 3、都不传
constructor() {
// console.log(props)
super();
console.log(this.props); // undefined
}
}
复制代码
总结:
构造器是否接收 props,是否传递给 super, 取决于:是否希望在构造器中通过 this 访问 props
五、三方库 prop-types 的使用
基础类型验证
PropTypesDemo.propTypes = {
propsArray: PropTypes.array, // 数组
propsObject: PropTypes.object, // 对象
propsString: PropTypes.string, //字符串
propsNumber: PropTypes.number, // 数字
propsBool: PropTypes.bool, // 布尔值
propsSymbol: PropTypes.symbol, // 私有数据类型
propsFunc: PropTypes.func, // 函数
// 节点数据类型(任何可以渲染的数据类型)
propsNode: PropTypes.node,
// react元素(jsx)
propsElement: PropTypes.element,
}
复制代码
React 中 对象 bool symbol func 都是不能直接渲染在页面上的这些数据类型都不属于 node 类型
必传属性修饰符 isRequired
prop-types 所有类型后丢可以跟 isRequired 修饰符代表该属性是必传属性
PropTypesDemo.propTypes = {
propsArray: PropTypes.array.isRequired, // 必传 Array 类型
propsElement: PropTypes.element.isRequired // 必传 element 类型
propsAny: PropTypes.any.isRequired // 必传 任意数据类型
}
复制代码
prop-types 还提供了一个 any 数据类型表示任意数据类型,该类型主要是配合 isRequired 修饰符,表示当前属性不能为空
复杂类型验证
PropTypesDemo.propTypes = {
// 数据为指定构造函数函数的实例
propsCurrentProto: PropTypes.instanceOf(Dog),
// 属性值为指定的值的其中之一
propsOneOf: PropTypes.oneOf(['男', '女']),
// 属性的数据类型为指定类型的其中之一
propsOneOfType: PropTypes.oneOfType([
PropTypes.array,
PropTypes.object,
PropTypes.instanceOf(RegExp),
PropTypes.oneOf(['男', '女'])
]),
// 指定每一项数据类型的数组
propsStringArray: PropTypes.arrayOf(PropTypes.string),
// 指定每一项键值对value数据类型的对象
propsDateObj: PropTypes.objectOf(PropTypes.instanceOf(Date)),
// 指定key和value数据类型的对象
propsCurrentObject: PropTypes.shape({
name: PropTypes.string, // 这个属性可以为缺省值
age: PropTypes.number.isRequired // 该属性在当前对象中必须存在
})
}
复制代码
除了 instanceOf,oneOf 以外其他几个验证规则可以互相嵌套, isRequired 修饰符依然可以在上述验证规则中使用 自定义验证规则
在 React 组件的 propTypes 属性中可以给指定的属性,设置一个验证函数实现一些自定义验证规则。自定义验证函数一般情况下接收三个参数:props,propName,componentName。
props :当前组件接收到的属性传参的对象集合
propName :使用当前自定义规则的属性名
componentName :当前组件名
当接收 props 的属性值不能通过验证规则时只需要向函数外部返回一个 Error 实例对象就好了。
案例: 实现自定义验证规则,传入的数据必须是字符串或者数字,字符串不能包含“fxxk”敏感字符,数字必须大于等于 18 小于等于 120。
ComponentC.propTypes = {
propsA: function (props, propName, componentName) {
let val = props[propName]
if(typeof val === 'string') {
if(/fxxk/.test(val)) {
return new Error(`组件:${componentName},中属性"${propName}"值为${val}包含敏感字符`)
}
}
else if(typeof val === 'number') {
if(val < 18 || val > 120 ){
return new Error(`组件:${componentName},中属性"${propName}"值为${val}不满足18-120区间`)
}
}else {
return new Error(`组件:${componentName},中属性"${propName}" 值不是字串或数字`)
}
}
}
复制代码
定义验证规则配合arrayOf
或者 ObjectOf
使用
自定义验证函数可以作为参数传递给 prop-types 库的 arrayOf 或者 ObjectOf 中对数组,对象进行遍历验证。这时该验证规则函数接收 5 个参数:propValue,key, componentName,location,propsFullName
例子: propsCustomArrayOf[2]
,propsCustomArrayOf.name
PropTypesDemo.propTypes = {
// arrayOf 或者 ObjectOf 自定义验证规则
propsCustomArrayOf: PropTypes.arrayOf(function (propValue, key, componentName, location, propsFullName) {
if(!/matchme/.test(propValue[key])) {
return new Error(`Failed prop type: Invalid prop '${propsFullName}' supplied to ${componentName}.Validation failed.`)
}
}),
propsCustomObjectOf: PropTypes.objectOf(function (propValue, key, componentName, location, propsFullName) {
if(!/matchme/.test(propValue[key])) {
return new Error(`Failed prop type: Invalid prop '${propsFullName}' supplied to ${componentName}.Validation failed.`)
}
}),
}
复制代码
六、小总结
每个组件对象都会有 props(properties 的简写)属性
组件标签的所有属性都保存在 props 中
通过标签属性从组件外向组件内传递变化的数据
注意: 组件内部不要修改 props 数据
使用 propTypes 属性并配合 prop-types 三方库实现 prop 验证(不用另外下载,已集成在脚手架中)
评论