React 中的 Props 和 PropTypes

Avatar of Kingsley Silas
Kingsley Silas

DigitalOcean 为您旅程的每个阶段提供云产品。 立即开始使用 200 美元的免费额度!

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
};

我们声明 titleurlauthor 的 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 应用程序中同时使用 propspropTypes。 希望这篇文章展示了它们对整个 React 的重要性,因为如果没有它们,当交互发生时,我们就没有任何东西可以传递到组件之间。 它们确实是 React 设计围绕的组件驱动和状态管理架构的核心部分。

propTypes 则是额外的奖励——就像一个内置的质量保证检查器,可以捕获错误并让我们知道它们的存在。 很高兴知道它们在我们工作时为我们保驾护航。