文章目录
- 安装 prop-types 库
- 给组件的 props 添加限制
- 给 Header 组件添加限制
- 给 List 组件添加限制
- 给 Item 组件添加限制
- 验证 props 限制
- 完整代码
- Header 组件完整代码
- List 组件完整代码
- Item 组件完整代码
本文实现对组件的 props 进行属性的类型和必要性的限制。
为什么要对 props 增加限制呢?是为了在他人使用我们编写、或者我们使用他人编写的的组件的时候,传入了错误的 props 属性类型,或者漏传了必要的属性,通过限制可以在控制台报出有意义的错误提示信息。
建议: 在实际开发工作中,给组件编写必要性的注释,或者单独编写开发文档,介绍组件的使用,并说明 props 属性的类型和必要性。
安装 prop-types 库
PropTypes 提供一系列验证器,可用于确保组件接收到的数据类型是有效的。当传入的 prop 值类型不正确时,浏览器控制台将会显示警告。
- 通过 npm 安装
npm install prop-types
- 通过 yarn 安装
yarn add prop-types
注意:出于性能方面的考虑,propTypes 仅在开发模式下进行检查。
给组件的 props 添加限制
下面我们对 TodoList 的组件接收的 props 进行类型和必要性的限制。
给 Header 组件添加限制
Header 组件接收一个函数类型的 addTodo 的 props 属性,而且是必须的。实现代码片段如下:
// file: src/components/Header/index.jsx
// 引入 prop-types 库
import PropTypes from "prop-types";
// 以静态属性的方式定义限制规则
static propTypes = {
addTodo: PropTypes.func.isRequired,
};
给 List 组件添加限制
List 组件接收以下两个 props 属性:
- TodoList:数组类型,且必须。
- updateTodo:函数类型,且必须。
实现代码片段如下:
// file: src/components/List/index.jsx
// 引入 prop-types 库
import PropTypes from "prop-types";
// 以静态属性的方式定义限制规则
static propTypes = {
todoList: PropTypes.array.isRequired,
updateTodo: PropTypes.func.isRequired,
};
给 Item 组件添加限制
Item 组件接收以下 props 属性:
- id:数字类型,且必须。
- name:字符串类型,且必须。
- done:布尔类型,且必须。
- updateTodo:函数类型,且必须。
实现代码片段如下:
// file: src/components/Item/index.jsx
// 引入 prop-types 库
import PropTypes from "prop-types";
// 以静态属性的方式定义限制规则
static propTypes = {
id: PropTypes.number.isRequired,
name: PropTypes.string.isRequired,
done: PropTypes.bool.isRequired,
updateTodo: PropTypes.func.isRequired,
};
至此我们就完成了对相关组件的 props 的限制。
扩展: 上面的代码只对 props 进行了简单的限制,如果你想要了解更多限制规则,请参考 React 官方介绍 使用 PropTypes 进行类型检查
验证 props 限制
下面我们以 Item 组件为例来验证上面添加的 PropTypes 限制是否起作用。
验证 id,它的限制规则是 数字且必须,那么我们把 App.js 中的初始化数据中的 id 改为字符串,代码如下:
// file: src/App.js
// 初始化状态
state = {
todoList: [
{ id: 1, name: "参加晨会", done: true },
{ id: "2", name: "A功能开发", done: true }, // 将这条 todo 的 id 改为字符串
{ id: 3, name: "B功能开发", done: false },
],
};
此时浏览器控制台报错如下:
其他组件的验证,可按照上面的方法进行。
完整代码
Header 组件完整代码
// file: src/components/Header/index.jsx
import React, { Component } from "react";
import PropTypes from "prop-types";
import { nanoid } from "nanoid";
import "./index.css";
export default class Header extends Component {
// 对接收的 props 进行:类型、必要性的限制
static propTypes = {
addTodo: PropTypes.func.isRequired,
};
// 键盘事件的回调
handleKeyUp = (event) => {
// 解构赋值获取 keyCode, target
const { keyCode, target } = event;
// 判断是否是回车按键
if (keyCode !== 13) return;
// 添加的任务名称不能为空
if (target.value.trim() === "") {
alert("任务名称不能为空");
return;
}
// 构建一个 Todo 对象
const todoObj = { id: nanoid(), name: target.value, done: false };
// 将 todoObj 传递给 App 组件
this.props.addTodo(todoObj);
// 清空输入
target.value = "";
};
render() {
return (
<div className="todo-header">
<input
onKeyUp={this.handleKeyUp}
type="text"
placeholder="请输入你的任务名称,按回车键确认"
/>
</div>
);
}
}
List 组件完整代码
// file: src/components/List/index.jsx
import React, { Component } from "react";
import PropTypes from "prop-types";
import Item from "../Item";
import "./index.css";
export default class List extends Component {
// 对接收的 props 进行:类型、必要性的限制
static propTypes = {
todoList: PropTypes.array.isRequired,
updateTodo: PropTypes.func.isRequired,
};
render() {
const { todoList, updateTodo } = this.props;
return (
<ul className="todo-main">
{todoList.map((todo) => {
return <Item key={todo.id} {...todo} updateTodo={updateTodo} />;
})}
</ul>
);
}
}
Item 组件完整代码
// file: src/components/Item/index.jsx
import React, { Component } from "react";
import PropTypes from "prop-types";
import "./index.css";
export default class Item extends Component {
// 对接收的 props 进行:类型、必要性的限制
static propTypes = {
id: PropTypes.number.isRequired,
name: PropTypes.string.isRequired,
done: PropTypes.bool.isRequired,
updateTodo: PropTypes.func.isRequired,
};
state = { mouse: false }; //标识鼠标移入、移出
// 鼠标移入、移出的回调
handleMouse = (flag) => {
return () => {
this.setState({ mouse: flag });
};
};
// 勾选、取消勾选某一个 Todo 的回调
handleCheck = (id) => {
return (event) => {
this.props.updateTodo(id, event.target.checked);
};
};
render() {
const { id, name, done } = this.props;
const { mouse } = this.state;
return (
<li
style={{ backgroundColor: mouse ? "#eee" : "#fff" }}
onMouseEnter={this.handleMouse(true)}
onMouseLeave={this.handleMouse(false)}
>
<label className="container">
<input
type="checkbox"
defaultChecked={done}
onChange={this.handleCheck(id)}
/>
<span>{name}</span>
</label>
<button
className="btn btn-danger btn-sm"
style={{ display: mouse ? "block" : "none" }}
>
删除
</button>
</li>
);
}
}