My App

属性委托

属性委托

在《组件的范围》课程中的 Banner 示例中,我们看到我们的 LoggedIn 横幅组件是如何“转发”一些属性的:

function LoggedInBanner({
  user,
  // These two props:
  type,
  children,
}) {
  if (!user || user.registrationStatus === "unverified") {
    return null;
  }
 
  // ...are forwarded along to Banner:
  return <Banner type={type}>{children}</Banner>;
}

如果这个组件需要转发的属性不是 2 个而是 10 个呢?我们是否必须逐个列出所有属性?

幸运的是,React 不会要求我们这么做。相反,我们可以利用剩余参数(rest parameters)和展开语法(spread syntax) 👀

效果如下:

function LoggedInBanner({
  user,
  // Collect all unspecified props:
  ...delegated
}) {
  if (!user || user.registrationStatus === "unverified") {
    return null;
  }
 
  // And pass them onto Banner:
  return <Banner {...delegated} />;
}

我选择名称 delegated ,因为我觉得它在语义上是合适的,但我们可以将这个变量命名为任何我们想要的名称。有些人更喜欢使用 rest :

function LoggedInBanner({
  user,
  ...rest
}) {
  if (!user || user.registrationStatus === "unverified") {
    return null;
  }
 
  return <Banner {...rest} />;
}

为了保持一致性,我将在整个课程中使用 delegated 。

如果我们对 delegated 变量进行 console.log ,我们将看到一个对象,其中包含提供给该组件的所有其他 props。例如:

console.log(delegated);
/*
  {
    type: 'success',
    children: 'Account registered!',
  }
*/

要将这些 props 应用到我们的 Banner 元素上,我们要创建一个用花括号包裹的表达式插槽,并使用展开语法( ... )来分布这些 props:

// This code:
<Banner {...delegated} />
 
// ...is equivalent to this code:
<Banner
  type={delegated.type}
  children={delegated.children}
/>
 
// ...which is the same thing as this code:
<Banner type={delegated.type}>
  {delegated.children}
</Banner>

为了进一步揭开这种新语法的神秘面纱,以下是如何将其转译为普通 JavaScript 的方式:

// This JSX...
<UserProfileCard user={currentUser} {...delegated} />;
 
// ...turns into this JavaScript:
React.createElement(UserProfileCard, {
  user: currentUser,
  ...delegated,
});

展开语法注意事项

在 React 社区中,像这样使用“尾随逗号”是很常见的:

const someObject = {
  id: 1234,
  createdAt: "2022/07/01",
  modifiedAt: "2022/07/02",
  avatar: "/src/avatar.png", // <-- Lil’ comma here!
};

我喜欢使用尾随逗号,因为这意味着当我在最后一项之后添加新的键/值对时,不必担心忘记加逗号(此外,它还能减少代码审查中的“干扰”!)

但是,如果你尝试在我们的 ...delegated 中使用尾随逗号,将会报错:

function Slider({
  label,
  ...delegated // <-- This comma is a problem
}) {}

Unexpected trailing comma after rest element. 在剩余元素之后出现意外的尾随逗号。

当我们使用像这样的剩余参数时,它需要是列表中的最后一个元素,因此 JavaScript 语言的设计者决定在此处不允许末尾的逗号。

这很烦人,不过幸运的是错误信息非常清楚,因此我们不应该在这个问题上浪费太多时间。

增强型 HTML 标签

本视频重新回顾了我们在模块 3 中学习 Hook 规则时创建的 TextInput 组件

这是视频中的沙盒:

import React from 'react';

import TextInput from './TextInput';

function LoginForm() {
  const [email, setEmail] = React.useState('');
  const [password, setPassword] = React.useState('');

  function handleLogin() {
    alert(`Logged in with ${email}`);
  }

  return (
    <form onSubmit={handleLogin}>
      <TextInput
        required={true}
        data-test-id="login-email-field"
        label="Email"
        type="email"
        value={email}
        onChange={(event) => {
          setEmail(event.target.value);
        }}
      />
      <TextInput
        required={true}
        minLength={12}
        label="Password"
        type="password"
        value={password}
        onChange={(event) => {
          setPassword(event.target.value);
        }}
      />
      <button>
        Submit
      </button>
    </form>
  );
}

export default LoginForm;

属性传递与 TypeScript

在 JavaScript 中,我们可以使用这种模式来捕获并转发所有 props 到底层的 DOM 节点上。但是在 TypeScript 中该如何实现呢?我们需要将每一个可能的属性都添加到我们的 props 接口当中吗?

幸运的是不需要!我们可以使用 ComponentProps 辅助工具。这已超出本课程的范围,但你可以在这篇 Matt Pocock 写的精彩文章“ComponentProps: React 最有用的类型辅助工具”中了解更多相关内容。

On this page