React_TS

新建项目

1
2
3
4
5
npx create-react-app my-app --template typescript

# or

yarn create react-app my-app --template typescript

安装 react-router

1
2
3
4
5
6
npm install --save react-router-dom
yarn add react-router-dom
//如果要支持ts
npm i -D @types/react-router-dom
//or
yarn add @types/react-router-dom -D

Ts 中别名的设置

很多时候引入模块想把src设置为@.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//vite.config.ts
import { defineConfig } from 'vite'
import path from 'path';
export default defineConfig({
resolve: {
alias: {
"@": path.resolve(__dirname, "src"),
},
},
}

//tsconfig.json
"baseUrl": "./",
"paths": {
"@/*": ["src/*"]
}

useState

对于存储string类型的 state,在useState右边加上<string>即可:

1
2
const [input, setInput] = useState<string>("");
//此处初始化是'',则ts会自动判断类型,不写<string>也行

如果要存储数组,就要用interface

1
2
3
4
5
[
{ id: "xxxx", content: "xxxxxxx", isDone: false },
{ id: "xxxx", content: "xxxxxxx", isDone: false },
{ id: "xxxx", content: "xxxxxxx", isDone: false },
];
1
2
3
4
5
6
interface TaskObj {
id: string;
content: string;
isDone: boolean;
}
const [task, setTask] = useState<TaskObj[]>([]);

继承父组件中 state 并有自己的初始值

image.png

父子传参

js 中传参直接在子组件上写,子组件通过props接收

1
<Doing doing={doing} setTask={setTask} />

但是 ts 会报错,因为子组件并没有满足传递参数的接口,需要定义接口

1
2
3
4
5
6
7
8
9
//添加的DoingProps表明该组件接收的参数需要满足
//有doing属性,且类型为数组,数组的元素满足TaskObj接口.
//有setTask属性,且类型是函数.

interface DoingProps {
doing: TaskObj[];
setTask: Function;
}
const Doing: React.FC<DoingProps> = ({doing,setTask}) => {...}

使用 context

尽量使用 context,使用 provider 去替代 props.

类型断言

拿输入框DOM节点的时候

1
2
3
4
5
const inputNode: HTMLInputELement = document.getELementById(`${id}`);
//可能会报错,加上类型断言即可
const inputNode: HTMLInputElement = document.getElementById(
`${id}`
) as HTMLInputElement;

事件

常用的 React 事件类型

  • React.MouseEvent - 点击事件
  • React.KeyboardEvent - 键盘事件
  • React.DragEvent - 拖拽事件
  • React.FocusEvent - 焦点事件
  • React.ChangeEvent - 表单域值变更事件
  • React.FormEvent - 表单提交事件
  • React.WheelEvent - 鼠标滚动事件
  • React.TouchEvent - 触摸事件

注意,有的不能加 react,因为加了 React 是合成事件,不加是原生事件.

键盘事件

1
const addTodo = (e: React.KeyboardEvent<HTMLInputElement>): void => {...}

输入事件

1
2
3
4
5
6
7
8
9
10
11
<input
ref={editInput}
onBlur={(e: React.FocusEvent<HTMLInputElement>) => onBlurEdit(e)}
className="text-edit"
style={{ display: state.onEdit ? "block" : "none" }}
value={todo.content}
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
handleTodoTextEdit(e, todo)
}
onKeyPress={(e: React.KeyboardEvent<HTMLInputElement>) => submitEditText(e)}
/>

e 的值可以是(e.target as HTMLInputElement).value.

细节的注意

泛型

泛型在类型或者接口后,是传入参数的约束.也就是说传入的参数是在这个泛型之内的.

返回类型

jsx 的返回类型是ReactElement
node 元素的返回类型是ReactNode

技巧

useState 中设置 setState 中的方法可以将参数解构,简化浅拷贝

1
onChange={e => setParam(...param, e.target.value)}

fetch 方法返回的是异步可以用 async 处理

1
2
3
4
5
fetch("").then(async (res) => {
if (res.ok) {
setList(await res.json());
}
});

修改值时可以先将该值拷贝,修改拷贝值,return 出来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const useArray = <T></T>(initialArray: T[]) => {
const [value, setValue] = useState(initialArray)

return {
value,
setValue,
add: (item: V) => {
setValue([...item, value])
},
clear: () => setValue([]),
removeIndex: (index: number) => {
const copy = [...value]
copy.splice(index,1)
setValue(copy)
}
}
}

自定义 http

当一个参数有默认值时,参数内自动变为可选

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
export const http = (
endpoint: string,
{ data, token, headers, ...customConfig }: Config = {}
) => {
const config = {
method: "GET",
headers: {
Authorization: token ? `Bearer ${token}` : "",
"Content-Type": data ? "applcation/json" : "",
},
...customConfig,
};

if (config.method.toUpperCase() === "GET") {
endpoint += `?${qs.stringify(data)}`;
} else {
config.body = JSON.stringify(data || {});
}

return window.fetch(`${apiUrl}/${endpoint}`, config).then(async (res) => {
if (res.status === 401) {
// 未登录或token失效
await auth.logout();
window.location.reload();
return Promise.reject({ message: "请重新登录" });
}

const data = await res.json();
if (res.ok) {
return data;
} else {
return Promise.reject(data);
}
});
};

export const useHttp = () => {
const { user } = useAuth();
return (...[endpoint, config]: Parameters<typeof http>) =>
http(endpoint, { ...config, token: user?.token });
};

关于 svg 的引入问题

cra 虽然声明了 svg 作为模块可以引入,但是仍会报错.
解决方法:
新建src/react-app-env.d.ts

1
/// <reference types="react-scripts" />