0. React typescript中组件的导入、导出形式
0.1 导入形式
import { TodoList } from './components/TodoList';
0.2 导出形式
export const TodoList: React.FC<IProps> = (props) => {
return (
<ul>
...
</ul>
);
};
1. 根目录新建types.d.ts文件
把所有的类型定义写在这里,在其他文件中使用类型时不需要导入,因为ts编译器会自动去这个文件里找类型
types.d.ts【类型统一开头大写】
type Todo = {
text: string;
complete: boolean;
};
type ToggleTodo = (selectedTodo: Todo) => void;
type AddTodo = (newTodo: string) => void;
2. e的类型
handleSubmit = (e) => {};我们经常会碰到要填写e的类型,在不同情境下e的类型不同
2.1 input框onChange监听时
e的类型是ChangeEvent
<input type="text" value={newTodo} onChange={handleChange} />
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
setNewTodo(e.target.value);
};
2.2 form表单onSubmit提交时
e的类型是FormEvent
<button type="submit" onClick={handleSubmit}>
Add Todo
</button>
const handleSubmit = (e: FormEvent<HTMLButtonElement>) => {
e.preventDefault();
addTodo(newTodo);
setNewTodo('');
};
3. useState更新state时的写法
3.1 state简单类型
const [newTodo, setNewTodo] = useState<string>('');
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
setNewTodo(e.target.value);
};
3.2 state是个数组
选中的那项, state中该项的complete改变, true => false, false => true
const [todos, setTodos] = useState(initalState);
const toggleTodo: ToggleTodo = (selectedTodo) => {
const newTodos = todos.map((todo) => {
if (todo === selectedTodo) {
return {
...todo,
complete: !todo.complete,
};
}
return todo;
});
setTodos(newTodos);
};
新增一个todo对象到todos数组中
const addTodo: addTodo = (newTodo) => {
setTodos([
...todos, // 把已有的todos展开
{ // 新的todo(类型是对象)
text: newTodo,
complete: false,
},
]);
};
4. 完整代码
4.1 App.tsx
import React, { Fragment, useState } from 'react';
import { TodoList } from './components/TodoList';
import { AddTodoForm } from './components/AddTodoForm';
const initalState: Todo[] = [
{
text: 'Walk the dog',
complete: true,
},
{
text: 'Write app',
complete: false,
},
];
const App: React.FC = () => {
const [todos, setTodos] = useState(initalState);
const toggleTodo: ToggleTodo = (selectedTodo) => {
const newTodos = todos.map((todo) => {
if (todo === selectedTodo) {
return {
...todo,
complete: !todo.complete,
};
}
return todo;
});
setTodos(newTodos);
};
const addTodo: AddTodo = (newTodo) => {
newTodo.trim() !== '' &&
setTodos([
...todos,
{
text: newTodo,
complete: false,
},
]);
};
return (
<Fragment>
<TodoList todos={todos} toggleTodo={toggleTodo} />
<AddTodoForm addTodo={addTodo} />
</Fragment>
);
};
export default App;
4.2 TodoList.tsx
import React from 'react';
import { TodoListItem } from './TodoListItem';
interface IProps {
todos: Todo[];
toggleTodo: ToggleTodo;
}
export const TodoList: React.FC<IProps> = ({ todos, toggleTodo }) => {
return (
<ul>
{todos.map((todo) => {
return (
<TodoListItem key={todo.text} todo={todo} toggleTodo={toggleTodo} />
);
})}
</ul>
);
};
4.3 TodoListItem.tsx
import React from 'react';
import './TodoListItem.css';
interface IProps {
todo: Todo;
toggleTodo: ToggleTodo;
}
export const TodoListItem: React.FC<IProps> = ({ todo, toggleTodo }) => {
return (
<li>
<label className={todo.complete ? 'complete' : undefined}>
<input
type="checkbox"
checked={todo.complete}
onChange={() => toggleTodo(todo)}
/>
{todo.text}
</label>
</li>
);
};
4.4 TodoListItem.css
.complete {
text-decoration: line-through;
}
4.5 AddTodoForm.tsx
import React, { useState, ChangeEvent, FormEvent } from 'react';
interface Props {
addTodo: AddTodo;
}
export const AddTodoForm: React.FC<Props> = ({ addTodo }) => {
const [newTodo, setNewTodo] = useState<string>('');
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
setNewTodo(e.target.value);
};
const handleSubmit = (e: FormEvent<HTMLButtonElement>) => {
e.preventDefault();
addTodo(newTodo);
setNewTodo('');
};
return (
<form action="">
<input type="text" value={newTodo} onChange={handleChange} />
<button type="submit" onClick={handleSubmit}>
Add Todo
</button>
</form>
);
};