发布于 2026-01-06 1 阅读
0

将 React 组件转换为 TypeScript

将 React 组件转换为 TypeScript

在这篇文章中,我将带你了解如何修改一个简单的组件,从而开始使用 TypeScript。我正在测试的WordSearch 游戏
用 CreateReactApp 构建的,所以我将按照他们的指南,学习如何在现有项目中启用 TypeScript。

首先,我们需要安装一些软件包,以便在项目中启用 TypeScript。

  • TypeScript——启用实际 TS 编译器的软件包
  • @types/node - 包含 Node.js 类型定义的包
  • @types/react - 包含 React 类型定义的包
  • @types/react-dom - 包含 React DOM 类型定义的包
  • @types/jest - 包含 Jest 类型定义的包

CreateReactApp 的文档我把这些作为运行时依赖项安装,但我认为它们应该放在开发依赖项下,所以我会把它们安装在这里 :)

我打算将 AddWord 组件转换为使用 TypeScript。该组件负责在单词搜索游戏的单词面板中添加新单词。

图像

以下是原始代码,可帮助您理解:

import React, {Fragment, useEffect, useRef, useState} from 'react';
import Add from '@material-ui/icons/Add';

const AddWord = ({onWordAdd}) => {
   const inputEl = useRef(null);
   const [newWord, setNewWord] = useState('');
   const [disable, setDisable] = useState(true);

   useEffect(() => {
       // A word is valid if it has more than a single char and has no spaces
       const isInvalidWord = newWord.length < 2 || /\s/.test(newWord);
       setDisable(isInvalidWord);
   }, [newWord]);

   function onAddClicked() {
       onWordAdd && onWordAdd(inputEl.current.value);
       setNewWord('');
   }

   function onChange(e) {
       const value = e.target.value;
       setNewWord(value);
   }

   return (
       <Fragment>
           <input
               type="text"
               name="new"
               required
               pattern="[Bb]anana|[Cc]herry"
               ref={inputEl}
               placeholder="Add word..."
               value={newWord}
               onChange={onChange}
           />
           <button onClick={onAddClicked} disabled={disable}>
               <Add></Add>
           </button>
       </Fragment>
   );
};

export default AddWord;
Enter fullscreen mode Exit fullscreen mode

首先,我将文件扩展名更改为 .tsx - src/components/AddWord.js > src/components/AddWord.tsx

启动应用时,我遇到了第一个类型错误:

TypeScript error in 
word-search-react-game/src/components/AddWord.tsx(4,19):
Binding element 'onWordAdd' implicitly has an 'any' type.  TS7031

   2 | import Add from '@material-ui/icons/Add';
   3 |
 > 4 | const AddWord = ({onWordAdd}) => {
     |                   ^
Enter fullscreen mode Exit fullscreen mode

让我们来解决这个问题。
问题在于组件没有声明它允许接收的 props 类型。我看到了两种解决方法。一种是使用 React.FC,另一种是将这个函数组件视为一个函数,因此将其类型定义为一个没有 React 专用类型定义的函数。阅读了Kent C. Dodds 关于此问题的文章,以及这篇StackOverflow 上的详细回答中关于使用 React.FC 的注意事项后,我决定采用传统的函数类型定义方式。
好的,所以我们需要定义 props 的类型。我倾向于使用接口而不是类型,因为我有面向对象编程的背景,我知道使用接口要灵活得多。
这个组件接收一个 prop,它是一个回调函数,带有一个字符串参数,并且没有返回值(我喜欢用“I”前缀来标记我的接口)。
我们的 props 接口如下所示:

interface IAddWordProps {
   onWordAdd: (value: string) => void;
}
Enter fullscreen mode Exit fullscreen mode

用法如下:

const AddWord = ({onWordAdd}: IAddWordProps) => {
...
Enter fullscreen mode Exit fullscreen mode

这个问题解决了,接下来处理下一个错误:

TypeScript error in 
word-search-react-game/src/components/AddWord.tsx(20,32):
Object is possibly 'null'.  TS2531

   18 |
   19 |     function onAddClicked() {
 > 20 |         onWordAdd && onWordAdd(inputEl.current.value);
      |    
Enter fullscreen mode Exit fullscreen mode

没错,`inputEl` 可能为空,那么我们该如何处理呢?
一般来说,我不喜欢抑制错误和警告。既然决定使用某个工具,就没必要在“禁用规则”配置上过于谨慎,所以我们来认真解决这个问题。
首先,我想给 `inputEl` 的引用设置一个类型,它可以是 `null`,也可以是一个具有泛型类型的 `React.RefObject` 接口。由于我们处理的是一个输入元素,所以它应该是 `HTMLInputElement`。现在 `inputEl` 的类型定义如下:

const inputEl: RefObject<HTMLInputElement> | null = useRef(null);
Enter fullscreen mode Exit fullscreen mode

然而,这并没有解决我们的主要问题。让我们继续。
解决这个问题的一个方法是使用可选链,这意味着我们需要预先了解并准备好代码,以便优雅地处理空指针。现在的处理程序如下所示:

function onAddClicked() {
   onWordAdd && onWordAdd(inputEl?.current?.value);
Enter fullscreen mode Exit fullscreen mode

但是这样做就破坏了我们之前定义的 props 接口,因为它期望接收一个字符串,而现在它也可以接收 undefined,所以让我们修复接口以支持这一点:

interface IAddWordProps {
    onWordAdd: (value: string | undefined) => void;
}
Enter fullscreen mode Exit fullscreen mode

搞定了。接下来处理下一个错误。

TypeScript error in 
word-search-react-game/src/components/AddWord.tsx(24,23):
Parameter 'e' implicitly has an 'any' type.  TS7006

   22 |     }
   23 |
 > 24 |     function onChange(e) {
      |                       ^
   25 |         const value = e.target.value;
Enter fullscreen mode Exit fullscreen mode

解决方法很简单——我给 e 添加了 ChangeEvent 类型。现在它看起来像这样:

function onChange(e: ChangeEvent<HTMLInputElement>) {
    const value = e.target.value;
    setNewWord(value);
}
Enter fullscreen mode Exit fullscreen mode

这不是“React 类型”,而且就目前而言,我看不出在不需要的时候使用 React 类型有什么理由(如果您知道这样的理由,请在评论中分享)。

搞定!应用程序已恢复运行 :)
下面您可以找到修改后的代码(添加了一些额外的非关键类型),您可以将其与本文开头的原始代码进行比较。

更新——
在收到下方评论区(以及Reddit上)的一些宝贵反馈后,我已据此对代码进行了一些修改。谢谢大家。

import React, {ChangeEventHandler, MouseEventHandler, RefObject, useRef, useState} from 'react';
import Add from '@material-ui/icons/Add';

interface IAddWordProps {
    onWordAdd?: (value: string | undefined) => void;
}

const AddWord = ({onWordAdd}: IAddWordProps) => {
    const inputEl: RefObject<HTMLInputElement> | null = useRef(null);
    const [newWord, setNewWord] = useState('');
    const [disable, setDisable] = useState(true);

    const onAddClicked: MouseEventHandler<HTMLButtonElement> = () => {
        onWordAdd?.(newWord);
        setNewWord('');
    };

    const onChange: ChangeEventHandler<HTMLInputElement> = ({currentTarget: {value}}) => {
        setNewWord(value);
        // A word is valid if it has more than a single char and has no spaces
        const isInvalidWord: boolean = value.length < 2 || /\s/.test(value);
        setDisable(isInvalidWord);
    };

    return (
        <>
            <input
                type="text"
                name="new"
                required
                pattern="[Bb]anana|[Cc]herry"
                ref={inputEl}
                placeholder="Add word..."
                value={newWord}
                onChange={onChange}
            />
            <button onClick={onAddClicked} disabled={disable}>
                <Add />
            </button>
        </>
    );
};

export default AddWord;
Enter fullscreen mode Exit fullscreen mode

谢谢 :)

嘿!如果你喜欢刚才读到的内容,也别忘了在推特上关注我哦 :)关注 @mattibarzeev 🍻

文章来源:https://dev.to/mbarzeev/converting-a-react-component-to-typescript-15cl