React 鼓励开发人员通过 将 UI 分解成组件 来构建应用程序。 这意味着始终需要将数据从一个组件传递到另一个组件——更具体地说,是从父组件传递到子组件——因为我们正在将它们拼接在一起,并且它们相互依赖。
React 将组件之间传递的数据称为 **props**,我们将深入探讨这些数据。 而且,既然我们谈论的是 props,那么任何关于该主题的文章如果不对 **PropTypes** 进行介绍都是不完整的,因为它们可以确保组件传递了完成工作所需正确的类型的数据。
有了这些,让我们一起解开这些基本但内容丰富的术语。
Props:传递的数据
基本上,props 是使 React 成为如此强大的工具的关键。 React 被设计成将事物分解成在需要时才提供的片段。 Props 是这些片段存储的定义特征,并在请求时被访问和发送。 结果是屏幕只渲染它需要的内容,不多不少,从而加快页面加载速度并提高整体性能。
这些数据可以采用不同的形式:即字符串、数组和函数。 组件之间传递数据的能力,让我们具体分解一下如何访问和传递数据。
传递和访问 Props
假设我们正在开发一个应用程序,该应用程序显示从 CodePen 获取的有趣演示列表。
查看 CodePen 上 Kingsley Silas Chijioke (@kinsomicrote) 编写的笔 Props Pen。
我们可以将应用程序描述为组件的集合。

笔列表需要数据,特别是应用程序显示的每个演示的标题、URL 和作者。 我们可以像这样构建数据。
const pensList = [
{
title: "Elastic Input[Google Chrome]",
url: "https://codepen.io/andreasstorm/pen/JBGWBa",
author: "Andreas Storm"
},
{
title: "Phenomenon instances!",
url: "https://codepen.io/cvaneenige/pen/ajNjaN",
author: "Colin van Eenige"
},
{
title: "cpc-forms experiment with css variables",
url: "https://codepen.io/terabaud/pen/YjwYKv",
author: "Lea Rosema"
},
{
title: "Nuotron Logo Animation with Hover Effect",
url: "https://codepen.io/YahiaRefaiea/pen/YjyZLm",
author: "Yahia Refaiea"
}
];
**App** 组件将提取数据。 这是该组件的基本结构。
const App = () => {
return (
<div>
<PenList pens={pensList} />
</div>
);
}
我们将笔的数组作为 prop 传递给 **PenList**(我们很快就会创建它)。 父组件(PenList)访问数据(penList),该数据 **作为 pens prop 传递** 给子组件(Pen)。
const PenList = props => {
return (
<React.Fragment>
<h2>Interesting Pens on CodePen</h2>
<ul>
{props.pens.map(pen => {
return (
<li key={pen.url}>
<Pen {...pen} />
</li>
);
})}
</ul>
</React.Fragment>
);
};
PenList 组件循环遍历 pens prop(props.pens
)以将每个项目作为 Pen 组件返回。 如果这是一个类组件,我们将用 this
作为 pens prop 的前缀,如下所示。
class PenList extends React.Component {
render() {
return (
<React.Fragment>
<h2>Interesting Pens on CodePen</h2>
<ul>
{
this.props.pens.map(pen => {
return (
<li key={pen.url}>
<Pen {...pen} />
</li>
)
})
}
</ul>
</React.Fragment>
)
}
}
接下来要注意的是示例中 key
的使用。 key 是我们可以分配给列表中每个项目的唯一标识符,以确保我们能够区分项目。 在这种情况下,我们将 key 映射到每个笔的 URL。 两个项目不可能相同,因此它是用于此目的的良好数据。
其余属性作为 prop 传递给 Pen 组件。 以下是 Pen 组件使用这些 prop 的方式。
const Pen = props => {
return (
<div>
<p>
[{props.title}]
</p>
<p>Made by: {props.author}</p>
</div>
);
};
请注意,我们正在构建 Pen 组件(const Pen
),而不是像 PenList 组件那样将其定义为类(class PenList
)。 因此,我们可以使用 props
访问值。 这是一个方便的简写,我们可以使用它而不是重新映射 Pen 到数据。 父组件已经拥有它,所以让我们直接传递它吧!
使用 Props 传递函数
我们刚刚了解了如何将数据数组作为 prop 从一个组件传递到另一个组件,但如果我们使用的是函数而不是数据呢? React 允许我们在组件之间传递函数,但这在技术上相当复杂。 尽管如此,它仍然是某些特定用例中需要执行的操作,值得我们研究。
让我们举一个简单的例子,比如一个允许您创建任务列表的应用程序。 您知道,一个待办事项列表,例如家务、项目或其他任何事情。 在此应用程序中,任务列表包含在 **App** 组件中,它是父组件。 **Todo** 组件将在此场景中作为子组件,其唯一工作是列出创建的每个任务。
在真正的待办事项列表中,我们不仅希望创建任务,还希望能够在创建任务后将其删除。 由于待办事项列表包含在 App 组件中,因此我们必须能够通过获取 id
来识别用户想要从列表中删除的特定项目,然后在 App 组件中删除该项目。
听起来复杂吗? 以下是我们的目标。
查看 CodePen 上 Kingsley Silas Chijioke (@kinsomicrote) 编写的笔 Props Pen 2。
分解成代码。
let todoCounter = 1;
class App extends React.Component {
state = {
list: [],
item: ""
};
handleInputChange = event => {
this.setState({ item: event.target.value });
};
handleSubmit = event => {
event.preventDefault();
const item = {
id: todoCounter++,
value: this.state.item.slice()
};
this.setState({
list: this.state.list.concat(item),
item: ""
});
};
handleRemove = id => {
this.setState({
list: this.state.list.filter(c => c.id !== id)
});
};
render() {
return (
<React.Fragment>
<h2>Add Todo</h2>
<div>
<input
type="text"
value={this.state.item}
onChange={this.handleInputChange}
/>
</div>
<div>
<button type="submit" onClick={this.handleSubmit}>
Add
</button>
</div>
<div>
<h3>Lists</h3>
<ul>
{this.state.list.map(item => {
return (
<li key={item.id}>
<Todo {...item} removeTodo={this.handleRemove} />
</li>
);
})}
</ul>
</div>
</React.Fragment>
);
}
}
请注意,我们在顶部定义了 todoCounter
并将其设置为 1。 我们创建它是为了能够为待办事项分配唯一的 key,就像我们在之前的示例中为笔列表使用 URL 一样。
删除任务的方法是在 App 组件中创建的。 在 render()
函数中,我们将待办事项属性作为 prop 传递给 Todo 组件。 我们还将 handleRemove()
函数作为名为 removeTodo()
的 prop 传递。 我们将在 Todo 组件中使用它,如下所示。
class Todo extends React.Component {
deleteTodo = id => {
this.props.removeTodo(id);
};
render() {
return (
<div>
{this.props.value}
<button onClick={() => this.deleteTodo(this.props.id)}>X</button>
</div>
);
}
}
我们必须将待办事项的 id
传递给 Todo 组件中的 removeTodo()
,因为如果没有它,我们就无法更新 App 组件的状态。 这本质上就是我们如何使用 prop 在组件之间传递函数——与我们使用数组的方式非常相似,区别在于我们传递的是功能而不是原始数据。
PropTypes
PropTypes 确保将正确的类型 prop 传递给组件——反之亦然,接收组件接收的是正确的类型 prop。
我们可以将它们想象成美式足球四分卫将球传给接球手的场景。 四分卫只想让他的球员接球。 而且,同样重要的是,四分卫希望接球手接住一个球——而不是一只猫、一个腌黄瓜或一辆出租车。 PropTypes 将确保传递的是正确的对象(一个球),并且它被传递给了正确的接球手(队伍中的球员)。
(如果现实生活中的美式足球有 PropTypes 就好了!)
要使用 PropTypes,您必须通过在命令行中运行 yarn add prop-types
将该包作为依赖项添加到您的应用程序中。
我们可以在显示有趣笔的应用程序中使用 PropTypes。 以下是我们如何在 Pen 组件中使用它的方法。
Pen.propTypes = {
title: PropTypes.string,
url: PropTypes.string,
author: PropTypes.string
};
我们声明 title
、url
和 author
的 prop 应该是字符串。 不是数字。 不是函数。 只能是字符串。
如果我们碰巧将 author
的 prop 设置为数字而不是字符串,如下所示。
author: PropTypes.number
……我们将得到一个错误。
Warning: Failed prop type: Invalid prop `author` of type `string` supplied to `Pen`, expected `number`.
因此,PropTypes 有助于捕获错误。 我们还可以通过使用 isRequired
来强制传递 prop。
Pen.propTypes = {
title: PropTypes.string.isRequired,
url: PropTypes.string.isRequired,
author: PropTypes.string.isRequired
};
您将需要的基本数据类型包括字符串、数字、布尔值、函数等。
Person.propTypes = {
email: PropTypes.string,
age: PropTypes.number,
availability: PropTypes.bool,
handleSubmit: PropTypes.func
}
还有更多可用的类型和大量的 文档 可供参考。
在 prop 可选(即不使用 isRequired
)的情况下,您可以设置一个默认值以确保传递了一些内容。
Developer.defaultProps = {
language: 'JavaScript'
}
这样,即使没有提供语言 prop,它在使用时也始终会有一个值。
总结
好吧,这是对 React 中 props
的广泛概述。 几乎可以肯定,您将在 React 应用程序中同时使用 props
和 propTypes
。 希望这篇文章展示了它们对整个 React 的重要性,因为如果没有它们,当交互发生时,我们就没有任何东西可以传递到组件之间。 它们确实是 React 设计围绕的组件驱动和状态管理架构的核心部分。
而 propTypes
则是额外的奖励——就像一个内置的质量保证检查器,可以捕获错误并让我们知道它们的存在。 很高兴知道它们在我们工作时为我们保驾护航。