小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
本文已参与「掘力星计划」,赢取创作大礼包,挑战创作激励金。
props 概念
props:属性,是一个 JavaScript 对象。
props
是调用方传递给组件的数据(类似于函数的形参),而state
是在组件内被组件自己管理的数据(类似于在一个函数内声明的变量)。props
是不可修改的,所有React
组件都必须像纯函数一样保护它们的props
不被更改。 由于props
是传入的,并且它们不能更改,因此我们可以将任何仅使用props
的React
组件视为pureComponent
,也就是说,在相同的输入下,它将始终呈现相同的输出。
class Person extends React.Component {
render() {
const { name, age, sex } = this.props;
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{sex}</li>
<li>年龄:{age}</li>
</ul>
);
}
}
ReactDOM.render(<Person name="jerry" age="19" sex="男" />, document.getElementById("test1"));
ReactDOM.render(<Person name="tom" age="18" sex="女" />, document.getElementById("test2"));
ReactDOM.render(<Person name="sophia" age="24" sex="女" />, document.getElementById("test3"));
复制代码
回顾三点运算符
let arr1 = [1, 3, 5, 7, 9];
let arr2 = [2, 4, 6, 8, 10];
console.log('展开数组', ...arr1); // 展开数组
let arr3 = [arr1, arr2]; // 数组合并
console.log('数组合并', arr3);
let copyArr = [...arr1]; // 数组复制
console.log('数组复制', copyArr);
// 函数实参
function log(x, y, z) {
console.log('函数实参', x, y, z)
}
let args = [1, 1, 1, 1, 1];
log(...args);
// 函数行参
function sum(...numbers) {
return numbers.reduce((pre, cur) => {
return pre + cur;
})
}
console.log('函数行参', sum(1, 2, 3, 4));
let obj1 = {a: 1, b: 2};
let obj2 = {c: 3, d: 4};
let obj3 = {...obj1, ...obj2}; // 对象合并
console.log('对象合并', obj3);
let copyObj = {...obj1}; // 对象复制
console.log('对象复制', copyObj);
let person = {name: 'tom', aget: '18'};
console.log('对象展开', ...person); // 对象展开失败
复制代码
上面这段代码介绍了三点运算符大部分用法:展开数组、数组合并、数组复制、对象合并、对象复制、函数实参、函数形参...
但是经过试验发现对象展开失败,可以看出直接写展开运算符展开对象是无法成功的,需要在外面包一层花括号复制该对象。所以:三点运算符无法直接遍历一个对象,但是三点运算符可以直接遍历一个数组并以一串字符的形式输出。
批量传递 props
当我们需要批量传递 props 时,可以使用到三点运算符去实现:
const p = {name: 'sophia', age: 24, sex: '女'}
ReactDOM.render(<Person {...p} />, document.getElementById("test"));
复制代码
但是要注意的是 <Person {...p} />
中的 {...p}
不等同于上一章节提到的对象展开失败的用法。React 渲染函数中的花括号是作为分隔符在使用,而真正写的 JS 只有 ...p
。
但是我们上面不是提到三点运算符无法直接遍历一个对象 吗?实际上 React + Babel 可以允许展开运算符在标签属性传递这里展开一个对象,但是原生 JS 是不可以的。
对 props 进行限制
// 引入 prop-types 包:通过 PropTypes 调用
class Person extends React.Component {
// 对标签属性进行类型和必要性的限制
static propTypes = {
name: PropTypes.string.isRequired,
sex: PropTypes.string,
age: PropTypes.number,
speak: PropTypes.func, // 由于 function 是关键字,所以内置类型为 func
}
// 指定默认标签属性值
static defaultProps = {
sex: '女',
age: 24
}
render() {
const { name, age, sex } = this.props;
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{sex}</li>
<li>年龄:{age}</li>
</ul>
);
}
}
const p = {name: 'sophia', age: 24, sex: '女'}
ReactDOM.render(<Person {...p} />, document.getElementById("test"));
ReactDOM.render(<Person name="jerry" age={19} speak={speak} />, document.getElementById("test1"));
ReactDOM.render(<Person name="tom" age={18} sex="女" />, document.getElementById("test2"));
ReactDOM.render(<Person name="sophia" age={24} sex="女" />, document.getElementById("test3"));
复制代码
其实对 props 的限制原本可以使用 Person.propTypes
和 Person.defaultProps
在 Person 类的外部进行定义,但是由 React 基础小知识:类可以知道直接将属性赋值到类上的方式其实就是类的静态属性,因此可以使用 static 关键字进行定义。
构造器中的 props
首先我们要知道构造器有什么作用?
在官网上已经明确了构造器的两个作用,不过上面两种情况我们都可以用更方便的方式进行替代:
- 直接在类里
state = {isHot: false}
进行初始化状态。 - 直接在类里通过赋值语句配合箭头函数
changeWeather = () => {...}
实现自定义方法。
因此构造器其实可以省略
那
super(props)
的作用是什么?
class Person extends React.Component {
constructor(props) {
super(props)
console.log(this.props) // 输出定义属性
console.log(this.props) // this 可以省略
}
// constructor() {
// super()
// console.log(this.props) // undefined
// }
}
复制代码
通过上面例子可以证明,构造器是否接收 props,是否传递给 super,取决于是否希望在构造器中通过 this 访问 props。
如果不初始化 state 或不进行方法绑定,则不需要为 React 组件实现构造函数。
其实在开发中很少需要自定义构造器,因此能不写就可以不写。
函数式组件使用 props
组件实例三大属性:state、props、refs,因为类式组件中的实例拥有 this,所以可以通过 this 访问 state、props、refs。
对于函数式组件来说,它没有 this,因此也就无法使用 state、refs,但是依然可以使用 props,因为函数可以接收参数。
function Person (props) {
const { name, age, sex } = props;
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{sex}</li>
<li>年龄:{age}</li>
</ul>
);
}
const p = {name: 'sophia', age: 24, sex: '女'}
ReactDOM.render(<Person {...p} />, document.getElementById("test"));
ReactDOM.render(<Person name="jerry" age={19} speak={speak} />, document.getElementById("test1"));
ReactDOM.render(<Person name="tom" age={18} sex="女" />, document.getElementById("test2"));
ReactDOM.render(<Person name="sophia" age={24} sex="女" />, document.getElementById("test3"));
复制代码