从头开始创建您的第一个 React Typescript 项目 本教程附带的其他资源 获取您的环境设置 使用 create-react-app 进行引导 探索引导应用程序 精简为“Hello World” 我们的待办事项列表应用程序的快速模拟 创建待办事项列表项 切换待办事项 创建 TodoList 组件 添加待办事项 结论

2025-06-07

从头开始创建你的第一个 React Typescript 项目

本教程附带的其他资源

设置你的环境

使用 create-react-app 进行引导

探索引导应用程序

精简为“Hello World”

我们的待办事项列表应用程序的快速模拟

创建待办事项列表项

切换待办事项

创建 TodoList 组件

添加待办事项

结论

今天我们将学习如何编写一个 React Typescript 应用程序。正如任何新的前端语言或框架的传统,我们将学习如何编写一个待办事项列表应用程序!尽管待办事项列表应用程序的教程已经太多了,但我还是喜欢使用它,因为你可以将它与你曾经使用过的其他框架进行同类比较。


如果您喜欢本教程,请给予💓、🦄或🔖并考虑:


本教程附带的其他资源

本教程附带一个 GitHub 仓库!此外,如果您喜欢通过 YouTube 观看教程,我还录制了一个由三部分组成的 YouTube 教程系列。这两个视频都可以在下面找到:

设置你的环境

首先,需要满足几个先决条件。首先,如果你还没有安装 Node,则需要先安装。

通过在命令行中输入以下命令来确保已安装 Node node -v。您应该会看到版本信息。我的当前版本是 10.15.2,但您的版本可能有所不同。

node -v
Enter fullscreen mode Exit fullscreen mode

我们可以使用 npm 来管理 Node 包,但我更喜欢使用 yarn。因此,我打算使用 npm 全局安装 yarn:npm i -g yarn

npm i -g yarn
Enter fullscreen mode Exit fullscreen mode

如果成功,你应该可以通过输入以下命令来查看你的 yarn 版本yarn -v。同样,你的版本可能与我的不同:

yarn -v
Enter fullscreen mode Exit fullscreen mode

现在我们准备出发了!

使用 create-react-app 进行引导

为了省去设置的麻烦,让我们更快地开始,我们可以使用 来引导我们的应用create-react-app!我在生产环境中经常使用 React,但我通常还是会以它create-react-app作为模板来开始。

让我们使用 yarn 创建一个 React 应用。我们需要确保指定要使用 Typescript,并且要将应用命名为todo-list

yarn create react-app todo-list --template typescript
Enter fullscreen mode Exit fullscreen mode

你应该会看到一系列下载正在进行,最后会显示cd进入新目录并开始编码的指示。开始吧!

探索引导应用程序

确保您位于新todo-list目录中。您应该会看到以下文件夹和文件。虽然我们大部分工作都会在这个src文件夹中完成,但了解其他文件夹的作用也很重要。以下是简要概述:

  • node_modules - 包含您的应用程序使用的第三方库的代码。
  • 公共- 包含有助于构建最终应用程序的资产,包括index.html应用程序的图标等。
  • src——包含您最常使用的应用程序的源代码。
  • .gitignore - 指定源代码控制中要忽略哪些文件。
  • package.json - 包含您的应用程序的配置,包括依赖项和脚本等内容。
  • README.md - 从有关 create-react-app 的信息开始,但在实际应用程序中,您应该描述应用程序本身。
  • tsconfig.json - 包含 typescript 编译器的配置。
  • yarn.lock - 包含所有项目依赖项的确切版本。应将其纳入版本控制。

启动应用程序

太好了,浏览得够多了。让我们通过yarn start在命令提示符中运行来启动应用程序。

导航至http://localhost:3000,您应该会看到我们的应用程序的全部初始功能:

默认 create-react-app

注意:作为 create-react-app 的一部分,我们的应用会在每次修改时自动热加载!这意味着我们通常可以yarn start在控制台中保持运行,而无需重启它。实际上,我们会发现,当 TypeScript 编译器崩溃或我们添加或删除文件时,我们的应用偶尔需要重启服务器。

精简为“Hello World”

一切看起来很酷,但我们希望从本教程开始,相对来说比较新。因此,我们将从文件夹中删除一些文件src,并修改一些文件。

删除文件

cd src
rm App.css App.test.tsx index.css logo.svg serviceWorker.ts setupTests.ts
Enter fullscreen mode Exit fullscreen mode

剩下的文件应该是App.tsx、、index.tsxreact-app-env.d.ts

编辑代码

首先,我们来index.tsx删除对 Service Worker 的引用index.css。你的文件最终应该如下所示:

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);
Enter fullscreen mode Exit fullscreen mode

接下来,打开App.tsx并删除对 logo 和 CSS 文件的引用。此外,删除App函数中的所有内容,并将其替换为返回包含文本“Hello World”的 React 片段。

import React from 'react';

function App() {
  return <>Hello World</>;
}

export default App;
Enter fullscreen mode Exit fullscreen mode

现在查看我们的应用程序!

你好世界

我们的待办事项列表应用程序的快速模拟

React 的一个优点是你的组件结构通常可以紧密遵循你的设计。在我们的待办事项列表应用示例中,我们可以假设给出了以下模拟:

待办事项列表应用程序模拟

重要的是,我们可以看到我们的应用程序有一个TodoListItem、一个TodoList和一个AddTodoForm。最终,我们的应用程序结构将与此相对应。

创建待办事项列表项

让我们开始工作吧!TodoListItem.tsx在您的src文件夹中创建一个名为 的新文件。

让我们编写一个在列表项内包含占位符内容的基本 React 组件:

import React from 'react';

export const TodoListItem = () => {
  return <li>content</li>;
};
Enter fullscreen mode Exit fullscreen mode

酷!现在,让我们添加一些 props。从这里开始,我们就可以开始使用 TypeScript 了!我们的TodoListItem组件至少会接受一个todoitem 作为 props。这个todoitem 会有text一个属性,即string,以及一个complete属性,即boolean

一旦我们定义了我们的道具,我们就可以将其声明TodoListItem为一个功能组件(React.FC),然后将我们的Props作为泛型传递。

import React from 'react';

interface Todo {
  text: string;
  complete: boolean;
}

interface Props {
  todo: Todo;
}

export const TodoListItem: React.FC<Props> = props => {
  return <li>content</li>;
};
Enter fullscreen mode Exit fullscreen mode

接下来,让我们实际使用之前描述的 props。我们在每个列表项中放置一个复选框。当 为 时,复选框将被选中todo.completetrue标签将使用我们的 进行填充todo.text

另外,如果待办事项已完成,我们可以为其添加一个删除线。我们可以使用style属性来实现这一点。

import React from 'react';

interface Todo {
  text: string;
  complete: boolean;
}

interface Props {
  todo: Todo;
}

export const TodoListItem: React.FC<Props> = ({ todo }) => {
  return (
    <li>
      <label
        style={{ textDecoration: todo.complete ? 'line-through' : undefined }}
      >
        <input type="checkbox" checked={todo.complete} /> {todo.text}
      </label>
    </li>
  );
};
Enter fullscreen mode Exit fullscreen mode

创建类型声明文件

虽然我们可以Todo在这个文件中保留声明,但它会在整个应用程序中使用。我们可以在这里导出它,然后在应用程序中任何需要的地方导入它,或者创建一个类型声明文件。我们称之为“文件” types.d.ts,并将其放在我们的src文件夹中。文件的好处在于*.d.ts,我们的编译器会将其中的类型识别为项目的全局变量,我们无需显式地导入或导出它们。

类型.d.ts

interface Todo {
  text: string;
  complete: boolean;
}
Enter fullscreen mode Exit fullscreen mode

现在我们可以删除Todo声明的接口TodoListItem.tsx,一切仍然可以正常工作。

在我们的应用程序中包括 TodoListItem

当然,到目前为止我们只编写了一个组件;我们仍然需要将其包含在我们的应用程序中。我们现在就开始吧。转到App.tsx并导入组件。

import React from 'react';
import { TodoListItem } from './TodoListItem';

function App() {
  return (
    <>
      <TodoListItem />
    </>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

你可能会注意到,如果我们现在尝试运行我们的应用程序,它将编译失败——我们定义了我们的TodoListItem参数todo,但我们没有提供它!让我们改变这一点:我们将创建一个Todos数组。

我们将创建两个项目并将它们放入无序列表中:

import React from 'react';
import { TodoListItem } from './TodoListItem';

const todos: Todo[] = [
  {
    text: 'Walk the dog',
    complete: false,
  },
  {
    text: 'Write app',
    complete: true,
  },
];

function App() {
  return (
    <ul>
      <TodoListItem todo={todos[0]} />
      <TodoListItem todo={todos[1]} />
    </ul>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

现在让我们在浏览器中检查我们的应用程序:

待办事项应用程序的开头

切换待办事项

接下来我们想要实现切换待办事项的功能。我们不能再依赖todos数组,而是需要一些状态来管理它们。为此,我们将useStateApp.tsx文件中使用 React hook。我们可以将todos数组重命名为,initialTodos因为它实际上只是表示初始状态。

import React, { useState } from 'react';
import { TodoListItem, Todo } from './TodoListItem';

const initialTodos: Todo[] = [
  {
    text: 'Walk the dog',
    complete: false,
  },
  {
    text: 'Write app',
    complete: true,
  },
];

function App() {
  const [todos, setTodos] = useState(initialTodos);
  return (
    <ul>
      <TodoListItem todo={todos[0]} />
      <TodoListItem todo={todos[1]} />
    </ul>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

我们希望能够切换待办事项。我们可以toggleTodoApp.tsx文件中创建一个函数来实现。该toggleTodo函数将接受选定的待办事项,并切换complete该待办事项的 prop。

然后,我们可以传递toggleTodo给 each TodoListItem

import React, { useState } from 'react';
import { TodoListItem } from './TodoListItem';

const initialTodos: Todo[] = [
  {
    text: 'Walk the dog',
    complete: false,
  },
  {
    text: 'Write app',
    complete: true,
  },
];

function App() {
  const [todos, setTodos] = useState(initialTodos);

  const toggleTodo = (selectedTodo: Todo) => {
    const newTodos = todos.map(todo => {
      if (todo === selectedTodo) {
        return {
          ...todo,
          complete: !todo.complete,
        };
      }
      return todo;
    });
    setTodos(newTodos);
  };

  return (
    <ul>
      <TodoListItem todo={todos[0]} toggleTodo={toggleTodo} />
      <TodoListItem todo={todos[1]} toggleTodo={toggleTodo} />
    </ul>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

我们的 linter 现在很生气。这是因为toggleTodo不是我们 的预期 prop TodoListItem。让我们将其添加为预期 prop。趁此机会,我们在文件ToggleTodo中声明一个类型types.d.ts

类型.d.ts

interface Todo {
  text: string;
  complete: boolean;
}

type ToggleTodo = (selectedTodo: Todo) => void;
Enter fullscreen mode Exit fullscreen mode

现在,当我们将其添加toggleTodo为的道具时TodoListItem,让我们在元素onClick的处理程序中执行它input

TodoListItem.tsx

import React from 'react';

interface Props {
  todo: Todo;
  toggleTodo: ToggleTodo;
}

export const TodoListItem: React.FC<Props> = ({ todo, toggleTodo }) => {
  return (
    <li>
      <label
        style={{ textDecoration: todo.complete ? 'line-through' : undefined }}
      >
        <input
          type="checkbox"
          checked={todo.complete}
          onClick={() => {
            toggleTodo(todo);
          }}
        />{' '}
        {todo.text}
      </label>
    </li>
  );
};
Enter fullscreen mode Exit fullscreen mode

让我们打开应用程序并开始切换待办事项。成功了!

切换待办事项

创建 TodoList 组件

如果您还记得的话,我们的应用程序模拟包含一个TodoList包含所有待办事项的组件。

待办事项列表应用程序模拟

让我们创建这个组件。它需要接受以下 props:

  • todos映射的列表
  • toggleTodo传递给每个待办事项的函数

在这个组件中需要注意的是,我们映射了 ,todos而不是逐个列出它们。这显然是个好主意,因为理论上我们可以有任意数量的todos。需要注意的是,当我们迭代 时todos,我们会给每个 传递TodoListItem一个keyprop。这是 React 的 diffing 算法所需要的,它用于协调元素数组。

待办事项列表.tsx

import React from 'react';
import { TodoListItem } from './TodoListItem';

interface Props {
  todos: Todo[];
  toggleTodo: ToggleTodo;
}

export const TodoList: React.FC<Props> = ({ todos, toggleTodo }) => {
  return (
    <ul>
      {todos.map(todo => (
        <TodoListItem key={todo.text} todo={todo} toggleTodo={toggleTodo} />
      ))}
    </ul>
  );
};
Enter fullscreen mode Exit fullscreen mode

App.tsx现在,我们可以用 替换文件中的大部分代码了TodoList。我们必须记住传递正确的 props 给它——虽然如果我们忘记了,TypeScript 编译器会警告我们,这很棒!

应用程序.tsx

import React, { useState } from 'react';
import { TodoList } from './TodoList';

const initialTodos: Todo[] = [
  {
    text: 'Walk the dog',
    complete: false,
  },
  {
    text: 'Write app',
    complete: true,
  },
];

function App() {
  const [todos, setTodos] = useState(initialTodos);

  const toggleTodo = (selectedTodo: Todo) => {
    const newTodos = todos.map(todo => {
      if (todo === selectedTodo) {
        return {
          ...todo,
          complete: !todo.complete,
        };
      }
      return todo;
    });
    setTodos(newTodos);
  };

  return <TodoList todos={todos} toggleTodo={toggleTodo} />;
}

export default App;
Enter fullscreen mode Exit fullscreen mode

如果我们在浏览器中打开我们的应用程序,我们应该能够确认一切正常。

添加待办事项

让我们创建一个名为 的新组件AddTodoForm,以便添加待办事项。目前,我们只需创建一个不执行任何操作的表单并将其添加到App.tsx文件中。

AddTodoForm.tsx

import React from 'react';

export const AddTodoForm: React.FC = () => {
  return (
    <form>
      <input type="text" />
      <button type="submit">Add Todo</button>
    </form>
  );
};
Enter fullscreen mode Exit fullscreen mode

应用程序.tsx

import React, { useState } from 'react';
import { TodoList } from './TodoList';
import { AddTodoForm } from './AddTodoForm';

const initialTodos: Todo[] = [
  {
    text: 'Walk the dog',
    complete: false,
  },
  {
    text: 'Write app',
    complete: true,
  },
];

function App() {
  const [todos, setTodos] = useState(initialTodos);

  const toggleTodo = (selectedTodo: Todo) => {
    const newTodos = todos.map(todo => {
      if (todo === selectedTodo) {
        return {
          ...todo,
          complete: !todo.complete,
        };
      }
      return todo;
    });
    setTodos(newTodos);
  };

  return (
    <>
      <TodoList todos={todos} toggleTodo={toggleTodo} />
      <AddTodoForm />
    </>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

现在我们可以在浏览器中看到表单出现了。当我们尝试添加待办事项并点击提交时,除了页面重新加载之外什么也没有发生。

带有表格

现在,让我们让表单添加内容。首先,我们可以addTodoApp.tsx文件中创建一个函数,该函数最终将传递给表单。我们可以AddTodotypes.d.ts文件中声明其类型。

由于每个新事物todo一开始都是不完整的,我们实际上只需要text支撑来创建一个。

类型.d.ts

interface Todo {
  text: string;
  complete: boolean;
}

type ToggleTodo = (selectedTodo: Todo) => void;

type AddTodo = (text: string) => void;
Enter fullscreen mode Exit fullscreen mode

应用程序.tsx

import React, { useState } from 'react';
import { TodoList } from './TodoList';
import { AddTodoForm } from './AddTodoForm';

const initialTodos: Todo[] = [
  {
    text: 'Walk the dog',
    complete: false,
  },
  {
    text: 'Write app',
    complete: true,
  },
];

function App() {
  const [todos, setTodos] = useState(initialTodos);

  const toggleTodo: ToggleTodo = (selectedTodo: Todo) => {
    const newTodos = todos.map(todo => {
      if (todo === selectedTodo) {
        return {
          ...todo,
          complete: !todo.complete,
        };
      }
      return todo;
    });
    setTodos(newTodos);
  };

  const addTodo: AddTodo = (text: string) => {
    const newTodo = { text, complete: false };
    setTodos([...todos, newTodo]);
  };

  return (
    <>
      <TodoList todos={todos} toggleTodo={toggleTodo} />
      <AddTodoForm addTodo={addTodo} />
    </>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

再次,我们会在此时遇到一个熟悉的编译错误:AddTodoFrom不期望addTodoprop,所以编译器报错。好!让我们通过将 prop 添加到 来解决这个问题AddTodoForm

import React from 'react';

interface Props {
  addTodo: AddTodo;
}

export const AddTodoForm: React.FC<Props> = ({ addTodo }) => {
  return (
    <form>
      <input type="text" />
      <button type="submit">Add Todo</button>
    </form>
  );
};
Enter fullscreen mode Exit fullscreen mode

现在我们的编译器错误已经消失,但我们的表单仍然没有任何反应。为了让它正常工作,我们需要做以下几件事:

  1. 使用维护内部text状态useState。这将允许我们维护新待办事项文本的状态。
  2. 绑定textinput值。
  3. setText在输入的处理程序中使用设置文本onChangee.target.value包含当前值。
  4. onClick向提交按钮添加处理程序以提交输入的文本。
  5. 确保取消实际提交表单的默认事件。
  6. addTodo使用并传递它来添加待办事项text
  7. text通过设置为空字符串来清除我们的表单。
import React, { useState } from 'react';

interface Props {
  addTodo: AddTodo;
}

export const AddTodoForm: React.FC<Props> = ({ addTodo }) => {
  const [text, setText] = useState('');

  return (
    <form>
      <input
        type="text"
        value={text}
        onChange={e => {
          setText(e.target.value);
        }}
      />
      <button
        type="submit"
        onClick={e => {
          e.preventDefault();
          addTodo(text);
          setText('');
        }}
      >
        Add Todo
      </button>
    </form>
  );
};
Enter fullscreen mode Exit fullscreen mode

就这样!回到应用程序,你现在应该能够添加新的待办事项并与它们进行交互了。

完成的应用程序

结论

感谢你的关注!希望这篇文章能帮助你开启使用 React 和 Typescript 打造精彩用户界面的旅程。

文章来源:https://dev.to/nas5w/creating-your-first-react-typescript-project-from-scratch-4f65
PREV
我如何在 6 个月内将开发者博客电子邮件注册人数从 0 增加到 1,050 在所有博客文章上请求注册 多写博客 写高质量的文章 即使其他人也写博客 关于内容的交叉发布到 Dev.to 发布到 Reddit... 如果你敢的话 每周发送一封电子邮件 制作视频教程 请求其他项目的注册 结论
NEXT
使用 Carbon 创建你自己的优雅代码截图