R

React 和 TypeScript 的三大陷阱

2025-06-10

React 和 TypeScript 的三大陷阱

近年来,React 和 TypeScript 的使用量呈爆炸式增长。这在目前看来并不令人意外。事实证明,这两种工具在各种规模的 Web 应用程序上都能有效运行,帮助开发人员满足各种业务需求。

随着 React 和 TypeScript 的火爆流行,工程师在日常工作中使用该技术栈时容易犯的错误也随之激增。这篇博客旨在分享我见过的开发人员在使用 React 和 TypeScript 时最容易犯的三个错误,以及如何避免它们。

让我们从最重要的一个开始。

使用React.FunctionComponentReact.FC

我经常看到组件被这样注释:

import * as React from 'react'

type Props = {
    // ...
}

const FirstComponent = React.FC<Props> = (props) => {
    // ...
}

const SecondComponent = React.FunctionComponent<Props> = (props) => {
    // ...
}
Enter fullscreen mode Exit fullscreen mode

乍一看,使用这些类型抽象React.FC来为组件定义类型似乎是个好主意。和的人体工程学React.FunctionComponent无疑很吸引人。默认情况下,它们为你提供了childrenprop、defaultPropspropTypes以及许多其他组件属性的类型。

您可以在此处阅读有关 React.FC 和 React.FunctionComponent 的更多信息

综上所述,我认为它们引入了不必要的复杂性,并且在类型方面过于宽容。

React.FC让我们先从使用 either或 的最关键问题开始React.FunctionComponent。我指的是它们带来的不必要的复杂性。这里有一个简单的问题:哪种类型的注释对你来说更直接、更容易理解?

我们明确注释组件参数的地方:

type Props = {
  // ...
};

const Component = (props: Props) => {
  // ...
};
Enter fullscreen mode Exit fullscreen mode

或者也许我们使用React.FC

import * as React from "react";

type Props = {
  // ...
};

const Component: React.FC<Props> = props => {
  // ...
};
Enter fullscreen mode Exit fullscreen mode

如果你熟悉React.FC,你可能会耸耸肩,说这两个选项完全有效。问题就在这里,主要在于“熟悉”或“缺乏”的概念。

接口React.FC很浅。大多数情况下,可以通过显式地注解 props 来替代。现在,想象一下,你刚接触一个被React.FC广泛使用的代码库,但你却不知道它的含义和作用。你很可能在第一天就不愿意修改Props该代码库中的类型定义。

这些类型引入的另一个问题是通过Props使用children属性扩充定义而产生的隐式可组合性。

我太喜欢 React 组件的可组合性了。如果没有这个children属性,就很难实现我最喜欢的 React 模式之一——复合组件模式。考虑到这一点,我认为我们通过隐式地定义组件的组合,会给它们的 API 引入误导。

import * as React from "react";

const MarketingButton: React.FC<{}> = () => {
  // Notice that I'm not using `props.children`
  return <span>Our product is the best!</span>;
};

// In a completely separate part of the codebase, some engineer tries to use the `MarketingButton`.
const Component = () => {
  return <MarketingButton>HELLO!??</MarketingButton>;
};
Enter fullscreen mode Exit fullscreen mode

使用 API 的工程师很可能会感到困惑,因为尽管能够以简单字符串的形式传递子组件MarketingButton,但更改不会反映在 UI 中。为了理解发生了什么,他们必须阅读组件的定义——这非常令人遗憾。这看起来像是一个牵强的例子,但想象一下,每天成千上万的工程师在经历我刚才描述的情况时浪费了多少时间。这个数字加起来真是可观!

错误输入 children 属性

在上一节中,我提到了childrenprop 的重要性。正确注释此属性对于简化其他开发人员的工作至关重要。

我个人有一条简单且对我有用的规则:

默认使用 React.ReactNode。如有需要,请更改

这是一个例子

type Props = {
  children: React.ReactNode;
};

const MarketingButton = ({ children }) => {
  return <button>{children}</button>;
};
Enter fullscreen mode Exit fullscreen mode

我发现自己很少选择不使用React.ReactNode,主要是为了进一步限制道具的价值。你可以在这里找到很棒的资源,帮助你选择应该使用的道具children类型children

泄漏组件类型

您多久会遇到一次以下列方式编写的组件:

export type MyComponentProps = {
  // ...
};

export const MyComponent = (props: MyComponentProps) => {
  // ...
};

// Some other part of the codebase, possibly a test file.
import { MyComponentProps } from "../MyComponent";
Enter fullscreen mode Exit fullscreen mode

导出会MyComponentProps产生两个问题。

  1. 你必须为该类型想出一个名字。否则,你最终会得到一堆导出的同名符号。在这样的代码库中操作会很麻烦,因为你必须主动关注自动补全从哪里导入符号。
  2. 它可能会创建团队中的其他工程师可能不知道的隐式依赖关系。
    • 我可以更改类型的名称吗?
    • 该类型是否MyComponentProps在其他地方使用?

只要您保持道具类型不导出,就可以避免这些问题。

有一种机制可以让你无需使用export关键字即可提取给定组件的 props 类型。我指的是React.ComponentProps泛型。用法如下。

type Props = {
  // ...
};

export const MyComponent = (props: Props) => {
  // ...
};

// In another file.
import { MyComponent } from "../MyComponent";
type MyComponentProps = React.ComponentProps<typeof MyComponent>;
Enter fullscreen mode Exit fullscreen mode

过去两年,我一直在使用这项技术编写 React 和 TypeScript 代码,从未后悔过。您可以在我的另一篇博客文章中了解更多关于这种泛型类型在编写组件测试中的实用性。

概括

以上是我在野外遇到的三大陷阱。希望我的这些想法能对你有所帮助。

如果您发现我所写的内容不正确或想要澄清文章的某些部分,请联系我!

你可以在推特上找到我 - @wm_matuszewski

感谢您抽出时间。

鏂囩珷鏉ユ簮锛�https://dev.to/wojciechmatuszewski/top- Three-react-typescript-pitfalls-50l8
PREV
Why TypeScript is a better option than JavaScript when it comes to functional programming?
NEXT
24个前端性能优化技巧