props

React 元素为用户自定义组件时,它会将 JSX 中所接收的所有属性集中转换为单个对象传递给组件,这个对象就是props。例如上面示例中定义的组件Shelf在使用时就可以如下传递其中展示所需的props.booksprops所提供的主要功能就是父子组件之间的通信,允许父组件将任意合法的 Javascript 内容,例如字面量、对象、数组、函数等传递给子组件。

一个组件所接收到的props内容在组件的逻辑处理和组件渲染期间是只读的不可更改的。

在定义组件的props时,我们往往会为其定义一套类型来方便规定组件可以接收的属性。例如可以这样做。

// 使用type定义
type BookProps = {
  code: string;
  title: string;
  description?: string;
  recommended?: boolean;
  onSelect?: (code: string) => void;
};

//使用interface定义
interface BookProps {
  code: string;
  title: string;
  description?: string;
  recommended?: boolean;
  onSelect?: (code: string) => void;
}

就像上面示例中所列举的,如果使用 Typescript 来定义props参数的类型,是可以使用type定义一个类型或者使用interface定义一个接口的。这两种定义出来的props内容是没有区别的,区别仅在于在 Typescript 语言中typeinterface的区别。

Caution

在Typescript中,interface定义的接口默认是会自动拼合所有同名接口的,但type定义的类型则不支持重名,而且type还支持定义联合类型等更复杂的类型。所以在实际使用中,可以根据业务的实际需要来选择使用哪种方式,在这一点上,React和Typescript都没有做具体的规定。

在上面这个示例定义的props类型中,在使用的时候,可以像下面这样来使用。

<Book
  code="ISBN-839485"
  title="Live with React"
  recommended
  onSelect={(code) => console.debug("Selected Book: ", code)}
/>

在上面这个示例中,没有向组件Book中传递属性description,而且props类型中description定义的是可选属性,那么在组件中这个属性的取值就默认是undefined了。对于布尔类型的属性,如果其出现在了组件的属性列表中,但没有给定值,例如上面示例中的recommended,那么在组件中它的值就是true。这也算是 React 提供的一个特性之一。

给子组件传递props就跟在 HTML 中为标签元素设定属性一样。只是需要注意的是,如果传递的内容是使用""包裹的,那么传递的内容将是一个字面量,如果是使用{}包裹的,那么传递的内容将是一个对象或者数组。

在组件中接收props的时候,还常常利用 Javascript 中的解构语法来简化props中内容的调用,例如上面这个示例中,<Book>组件就可以这样定义。

function Book({
  code,
  title,
  description,
  // 可以利用赋予默认值的方式,来设置其中某个未提供属性的默认值
  recommended = false,
  onSelect,
}: BookProps) {
  return (
    <div>
      <h1>{title}</h1>
      <span>{code}</span>
    </div>
  );
}

children属性的传递

children属性在 React 中负责传递包裹在当前组件中的子元素。例如在下面这个用法中,<Child>组件就是以<Parent>组件的子组件身份出现的。

<Parent>
  <Child />
</Parent>

要在组件中获取到传递进来的children属性,也是通过props参数的。例如以下就是<Parant>组件的定义示例。

function Parent(props) {
  return <div>{props.children}</div>;
}

但是在定义props的类型的时候,却不必特意手动定义children属性,因为 React 提供了一个默认的接口PropsWithChildren来提供这个属性。其具体使用可以参考以下示例。

import { PropsWithChildren } from "react";

interface ParentProps {
  name: string;
}

export function Parent(props: PropsWithChildren<ParentProps>) {
  return (
    <div>
      <h1>{props.name}</h1>
      {props.children}
    </div>
  );
}

利用 React 中提供的这个接口可以很方便的给当前定义的props类型中增加children属性。