为了账号安全,请及时绑定邮箱和手机立即绑定

React+Typescript教程:零基础快速入门指南

标签:
React Typescript
概述

React+TypeScript教程涵盖了从环境搭建到基本概念与语法的全面介绍。文章详细讲解了如何安装Node.js和npm,使用Create React App初始化项目,以及安装和配置TypeScript。此外,还涉及了React组件、TypeScript类型定义以及高阶组件和Hooks的使用。

React+TypeScript环境搭建
安装Node.js和npm

在开始构建React+TypeScript应用之前,需要确保你的计算机上已经安装了Node.js和npm。Node.js是一个JavaScript运行时环境,npm是Node.js的包管理器。你可以从Node.js官方网站下载最新版本的Node.js,安装过程中会自动安装npm。

以下是安装Node.js和npm的一些常用命令:

node -v
npm -v
npm install -g npm

如果你使用的是Linux系统,可以使用以下命令来安装Node.js:

curl -sL https://deb.nodesource.com/setup_14.x | sudo -E bash -
sudo apt-get install -y nodejs
使用Create React App初始化项目

Create React App是一个官方推荐的工具,用于快速搭建React应用。你可以使用它来初始化一个新的React项目,并自动配置TypeScript支持。

安装Create React App

可以通过以下命令安装create-react-app

npm install -g create-react-app

初始化一个React项目

在命令行中执行以下命令来初始化一个React项目:

create-react-app my-app --template typescript
  • my-app是你的项目的名称。
  • --template typescript参数表示你希望使用TypeScript模板来初始化项目。

初始化完成后,你可以进入项目目录并运行项目:

cd my-app
npm start

这将启动开发服务器,并在浏览器中打开应用。

安装TypeScript并配置项目

在项目初始化之后,TypeScript已经自动配置好了。但为了确保一切正常,你可以检查一下项目中的TypeScript配置文件。

检查TypeScript配置

进入项目目录,你会看到一个tsconfig.json配置文件。这个文件定义了TypeScript编译器的选项。你可以根据需要调整配置。

确保tsconfig.json文件的内容如下:

{
  "compilerOptions": {
    "target": "ES6",
    "module": "ESNext",
    "strict": true,
    "jsx": "react",
    "moduleResolution": "node",
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "sourceMap": true,
    "baseUrl": "src",
    "allowJs": true,
    "outDir": "./dist",
    "rootDir": "./src",
    "incremental": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "lib": ["ESNext", "DOM"]
  },
  "include": ["src/**/*"]
}

安装额外依赖

有时候,你可能需要安装一些额外的依赖,例如@types/react@types/react-dom,这些是React的TypeScript类型定义。

npm install @types/react @types/react-dom --save-dev

运行项目

再次运行项目,确保一切正常:

npm start
基本概念与语法
React组件与State

在React中,组件是应用程序的基本构建块。组件可以被定义为函数或类。类组件可以有状态(state),而函数组件则没有。

类组件

类组件定义如下:

import React, { Component } from 'react';

class HelloMessage extends Component {
  state = {
    message: 'Hello, TypeScript!'
  };

  render() {
    return <div>{this.state.message}</div>;
  }
}

export default HelloMessage;

函数组件

函数组件定义如下:

import React from 'react';

const HelloMessage = () => {
  return <div>Hello, TypeScript!</div>;
};

export default HelloMessage;
TypeScript的基本类型与接口

TypeScript是一种静态类型语言,它允许你在编写代码时定义类型。这有助于减少错误并提高代码的可维护性。

基本类型

TypeScript支持多种基本类型,包括:

  • number:表示数字
  • string:表示字符串
  • boolean:表示布尔值
  • nullundefined:表示空值
  • void:表示无返回值
let age: number = 25;
let name: string = 'John Doe';
let isActive: boolean = true;
let nullValue: null = null;
let undefinedValue: undefined = undefined;
let noReturn: void;

接口(Interface)

接口用于定义对象的结构。它是一种定义对象的类型的方式。

interface User {
  id: number;
  name: string;
  email: string;
}

let user: User = {
  id: 1,
  name: 'John Doe',
  email: 'john@example.com'
};
props与TypeScript类型定义

在React中,组件可以通过props来传递数据。在TypeScript中,你可以为这些props定义类型。

定义props类型

interface Props {
  name: string;
  age: number;
}

const User = (props: Props) => {
  return (
    <div>
      <h1>{props.name}</h1>
      <p>{props.age}</p>
    </div>
  );
};

使用类型别名

你可以使用类型别名来定义props的类型。

type UserProps = {
  name: string;
  age: number;
};

const User = (props: UserProps) => {
  return (
    <div>
      <h1>{props.name}</h1>
      <p>{props.age}</p>
    </div>
  );
};

默认props

你可以为组件定义默认props。

const defaultProps = {
  name: 'Guest',
  age: 18
};

const User = (props: UserProps) => {
  return (
    <div>
      <h1>{props.name}</h1>
      <p>{props.age}</p>
    </div>
  );
};

User.defaultProps = defaultProps;
TypeScript类型推断与类型断言
组件类型推断

TypeScript在处理组件时会自动进行类型推断。例如,当你传递一个对象给组件时,TypeScript会尝试推断这个对象的类型。

const data = {
  name: 'John Doe',
  age: 30
};

const User = (props: { name: string; age: number }) => {
  return (
    <div>
      <h1>{props.name}</h1>
      <p>{props.age}</p>
    </div>
  );
};

<User data={data} />;

在这个例子中,TypeScript会推断data对象的类型为{ name: string; age: number; }

使用类型断言

类型断言允许你在编译时强制指定对象的类型。这在你确定某种类型但TypeScript无法推断时非常有用。

const name: any = 'John Doe';

// 类型断言
const nameString: string = name as string;

console.log(nameString); // 输出 'John Doe'

类型断言可以用于组件的props,以确保它们具有正确的类型。

interface UserProps {
  name: string;
  age: number;
}

const User = (props: UserProps) => {
  return (
    <div>
      <h1>{props.name}</h1>
      <p>{props.age}</p>
    </div>
  );
};

const data = {
  name: 'John Doe',
  age: 30
};

const userProps = data as UserProps;

<User userProps={userProps} />;
高阶组件与TypeScript
高阶组件介绍

高阶组件(Higher-Order Components,HOC)是一种高级的React技术,用于复用组件中的逻辑。HOC本身是一个函数,它接收一个组件作为输入,并返回一个新的、增强的组件。

简单的HOC示例

import React from 'react';

interface EnhancedComponentProps {
  count: number;
}

interface EnhancedComponentState {
  count: number;
}

const withCounter = <P extends object>(WrappedComponent: React.ComponentType<P>) => {
  class EnhancedComponent extends React.Component<P, EnhancedComponentState> {
    state = {
      count: 0
    };

    increment = () => {
      this.setState(prevState => ({
        count: prevState.count + 1
      }));
    };

    render() {
      return <WrappedComponent {...this.props} count={this.state.count} />;
    }
  }

  return EnhancedComponent;
};

const Greeting = (props: EnhancedComponentProps) => {
  return <div>{`Count is ${props.count}`}</div>;
};

const EnhancedGreeting = withCounter(Greeting);

export default EnhancedGreeting;

在这个例子中,withCounter是一个HOC,它接收一个组件WrappedComponent,并在外部添加了一个计数器逻辑。EnhancedGreeting是使用HOC增强后的组件。

为HOC添加类型

要为HOC定义类型,可以使用泛型。这允许你在定义HOC时指定组件的props类型。

import React from 'react';

interface EnhancedComponentProps {
  count: number;
}

interface EnhancedComponentState {
  count: number;
}

const withCounter = <P extends object>(WrappedComponent: React.ComponentType<P>) => {
  class EnhancedComponent extends React.Component<P, EnhancedComponentState> {
    state = {
      count: 0
    };

    increment = () => {
      this.setState(prevState => ({
        count: prevState.count + 1
      }));
    };

    render() {
      return <WrappedComponent {...this.props} count={this.state.count} />;
    }
  }

  return EnhancedComponent;
};

const Greeting = (props: EnhancedComponentProps) => {
  return <div>{`Count is ${props.count}`}</div>;
};

const EnhancedGreeting = withCounter(Greeting);

export default EnhancedGreeting;

通过使用泛型,你可以确保HOC中的EnhancedComponent组件和WrappedComponent组件的类型一致。

React Hooks与TypeScript
使用Hooks的基本知识

React Hooks允许你在不编写类的情况下使用React的状态和其他特性。它们为函数组件带来了可复用的功能。

基本Hook

useState是React中一个常用的Hook,用于在函数组件中添加状态。

import React, { useState } from 'react';

const Counter = () => {
  const [count, setCount] = useState<number>(0);

  const increment = () => {
    setCount(count + 1);
  };

  return (
    <div>
      <button onClick={increment}>Increment</button>
      <p>{count}</p>
    </div>
  );
};

export default Counter;

在这个例子中,useState返回一个包含当前状态和更新状态函数的数组。count是当前状态,setCount是更新状态的函数。

常用Hooks

  • useState<T>:添加状态
  • useEffect:执行副作用操作
  • useContext:订阅Context的变化
  • useReducer:处理更复杂的逻辑
  • useCallback:缓存函数
  • useMemo:缓存计算结果
  • useRef:访问DOM节点或直接访问一个引用值
钩子类型定义与使用

在TypeScript中,你可以为Hooks定义类型,以确保它们的正确使用。

useState定义类型

import React, { useState } from 'react';

const Counter = () => {
  const [count, setCount] = useState<number>(0);

  const increment = () => {
    setCount(count + 1);
  };

  return (
    <div>
      <button onClick={increment}>Increment</button>
      <p>{count}</p>
    </div>
  );
};

export default Counter;

在这个例子中,useState的类型被明确为number

useEffect定义类型

useEffect通常用于副作用操作,例如数据获取、订阅和手动DOM操作。

import React, { useState, useEffect } from 'react';

const Counter = () => {
  const [count, setCount] = useState<number>(0);

  useEffect(() => {
    document.title = `You clicked ${count} times`;
  }, [count]);

  const increment = () => {
    setCount(count + 1);
  };

  return (
    <div>
      <button onClick={increment}>Increment</button>
      <p>{count}</p>
    </div>
  );
};

export default Counter;

在这个例子中,useEffect的依赖数组被指定为[count],确保只有count变化时才会触发副作用。

实战项目示例
构建一个简单的Todo应用

现在我们来构建一个简单的Todo应用,使用TypeScript和React。这个应用将允许用户添加、编辑和删除待办事项。

项目结构

项目的基本结构如下:

my-todo-app/
├── public/
│   └── index.html
├── src/
│   ├── App.tsx
│   ├── index.tsx
│   ├── services/
│   │   └── todoService.ts
│   ├── components/
│   │   ├── TodoItem.tsx
│   │   └── TodoForm.tsx
│   ├── types/
│   │   └── TodoType.ts
├── tsconfig.json
└── package.json

Todo类型定义

首先,定义一个Todo类型。

// src/types/TodoType.ts
export interface Todo {
  id: number;
  text: string;
  isCompleted: boolean;
}

服务层

创建一个服务层来处理Todo的逻辑。

// src/services/todoService.ts
import { Todo } from '../types/TodoType';

let todos: Todo[] = [];

const addTodo = (text: string) => {
  const id = todos.length ? todos[todos.length - 1].id + 1 : 1;
  todos.push({ id, text, isCompleted: false });
};

const deleteTodo = (id: number) => {
  todos = todos.filter(todo => todo.id !== id);
};

const toggleTodo = (id: number) => {
  const todo = todos.find(todo => todo.id === id);
  if (todo) {
    todo.isTodo.isCompleted = !todo.isCompleted;
  }
};

export { addTodo, deleteTodo, toggleTodo };

TodoItem组件

创建一个TodoItem组件来显示单个待办事项。

// src/components/TodoItem.tsx
import React from 'react';
import { Todo } from '../../types/TodoType';
import { toggleTodo, deleteTodo } from '../../services/todoService';

interface TodoItemProps {
  todo: Todo;
}

const TodoItem: React.FC<TodoItemProps> = ({ todo }) => {
  const toggle = () => {
    toggleTodo(todo.id);
  };

  const remove = () => {
    deleteTodo(todo.id);
  };

  return (
    <li>
      <input type="checkbox" checked={todo.isCompleted} onChange={toggle} />
      <span>{todo.text}</span>
      <button onClick={remove}>Remove</button>
    </li>
  );
};

export default TodoItem;

TodoForm组件

创建一个TodoForm组件来添加新的待办事项。

// src/components/TodoForm.tsx
import React, { useState } from 'react';
import { addTodo } from '../../services/todoService';

const TodoForm: React.FC = () => {
  const [text, setText] = useState('');

  const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
    event.preventDefault();
    addTodo(text);
    setText('');
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        value={text}
        onChange={event => setText(event.target.value)}
      />
      <button type="submit">Add Todo</button>
    </form>
  );
};

export default TodoForm;

App组件

最后,创建一个App组件来组装整个待办事项应用。

// src/App.tsx
import React, { useEffect, useState } from 'react';
import './App.css';
import { Todo } from '../types/TodoType';
import TodoForm from '../components/TodoForm';
import TodoItem from '../components/TodoItem';
import { addTodo, deleteTodo, toggleTodo } from '../services/todoService';

const App: React.FC = () => {
  const [todos, setTodos] = useState<Todo[]>(todos);

  useEffect(() => {
    setTodos(todos);
  }, [todos]);

  const toggleTodo = (id: number) => {
    setTodos(prevTodos => {
      return prevTodos.map(todo => {
        if (todo.id === id) {
          todo.isCompleted = !todo.isCompleted;
        }
        return todo;
      });
    });
  };

  const deleteTodo = (id: number) => {
    setTodos(prevTodos => prevTodos.filter(todo => todo.id !== id));
  };

  return (
    <div className="App">
      <h1>Todo App</h1>
      <TodoForm />
      <ul>
        {todos.map(todo => (
          <TodoItem key={todo.id} todo={todo} />
        ))}
      </ul>
    </div>
  );
};

export default App;

index.tsx

确保index.tsx文件正确导入并渲染App组件。

// src/index.tsx
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

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

reportWebVitals();

public/index.html

<!-- public/index.html -->
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Todo App</title>
  </head>
  <body>
    <div id="root"></div>
  </body>
</html>

reportWebVitals.ts

展示reportWebVitals函数的具体实现:


// src/reportWebVitals.ts
import { WebVitals } from '@firebase/analytics/dist/esm/types';
import { useEffect } from 'react';

const reportWebVitals = (onPerfEntry?: (entry: WebVitals) => void) => {
  if (onPerfEntry === undefined) {
    return;
  }

  const timer = Date.now();
  const handle = (entry: WebVitals) => {
    onPerfEntry(entry);
    window.performance.mark(`${entry.name}-${timer}`);
  };

  useEffect(() => {
    const handleTimeOrigin = (performance: Performance) => {
      performance.getEntriesByType('navigation').forEach(entry => {
        handle({
          id: entry.name,
          label: entry.name,
          name: entry.name,
          duration: entry.responseEnd - entry.startTime,
          startTime: entry.startTime,
          type: 'navigation',
          id: entry.name,
        });
      });

      performance.getEntriesByType('paint').forEach(entry => {
        handle({
          id: entry.name,
          label: entry.name,
          name: entry.name,
          duration: entry.startTime - timer,
          startTime: entry.startTime,
          type: 'paint',
          id: entry.name,
        });
      });
    };

    const performance = window.performance;
    if (performance) {
      handleTimeOrigin(performance);
    }
  }, []);
};

export default reportWebVitals;
``

通过以上步骤,你可以构建一个简单的Todo应用,使用TypeScript和React来定义类型和组件。这个示例展示了如何使用TypeScript来增强React应用的类型安全性,并确保代码的清晰和可维护性。
点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消