文章目录
- 效果
- 功能-状态管理
- 相关接口定义
- 相关方法定义
- UI
- input输入框:回车添加todo
- 标题
- 列表
- 列表项
- Main
- 总体代码
非常简单入门的react-todo练习,代码写的很小白。
效果
技术栈:react-typeScript
- 数据分为代办Todo和已办完Done,可以点击复选框切换状态
- 可以添加代办事件
- 会用到
useState
和useReducer
,详情看状态管理 – React 中文文档
接下来是代码实现。
功能-状态管理
相关参考:
TypeScript中的标记联合类型:实现Todo代办-CSDN博客
迁移状态逻辑至 Reducer 中 – React 中文文档 (docschina.org)
相关接口定义
Todo的状态表示:
text表示代办内容,done表示是否完成,id表示它在列表中的位置。
interface Todo {
readonly text: string;
readonly done: boolean;
readonly id: number;
}
对Todo的操作表示:
interface AddTodo {
type: "Add_TODO";
text: string;
}
interface ToggleTodo {
type: "TOGGLE_TODO";
index: number;
}
// 操作只有添加todo和修改todo两种
type TodoAction = AddTodo | ToggleTodo;
相关方法定义
操作Todo的方法:
// 点击修改状态
function todoReducer(list: Array<Todo>, action: TodoAction): Array<Todo> {
switch (action.type) {
case "Add_TODO": {
return [
...list,
{
text: action.text,
done: false,
id: list.length,
},
];
}
case "TOGGLE_TODO": {
return list.map((item, index) => {
if (index !== action.index) {
return item;
}
return {
text: item.text,
done: !item.done,
id: item.id,
};
});
}
default: {
return list;
}
}
}
useReducer
部分:写在比较父级的组件中。把对应方法用prop的方式传给需要用的子组件。此博客写在了Main
组件中。
// useReducer
const [todoList, dispatch] = useReducer(todoReducer, initTodo);
function handleAddTodo(text: string) {
dispatch({
type: "Add_TODO",
text: text,
});
}
function handleToggleTodo(index: number) {
dispatch({
type: "TOGGLE_TODO",
index: index,
});
}
模拟的数据:initTodo
// 模拟数据
const initTodo: Array<Todo> = [
{
text: "吃饭",
done: true,
id: 0,
},
{
text: "睡觉",
done: true,
id: 1,
},
{
text: "上班",
done: true,
id: 2,
},
{
text: "下班",
done: false,
id: 3,
},
{
text: "运动",
done: false,
id: 4,
},
{
text: "听歌",
done: false,
id: 5,
},
];
UI
react是非常组件化的框架。此Todo可以拆成好几个组件:
- input输入框:回车添加todo
- 标题
- 列表:分开显示Todo/Done的列表
- 列表项
input输入框:回车添加todo
这里需要用useState
管理input输入的状态。
注意:每个input框都要绑定onChange或readonly,否则会报错。
当回车时,添加输入框中的值到Todo的列表,即触发前面定义的handleAddTodo
方法。此方法在这里没有定义,因此要从定义它的地方(Main
)传过来。
function Add({ handleAddTodo }: { handleAddTodo: Function }) {
const [inputValue, setInputValue] = useState("");
return (
<>
<div className="header">
<input
type="text"
value={inputValue}
onChange={(e) => {
setInputValue(e.target.value);
}}
onKeyDown={(e) => {
if (e.key === "Enter") {
handleAddTodo(inputValue);
setInputValue("");
}
}}
placeholder="在这里添加新的代办事件"
/>
</div>
</>
);
}
标题
// 标题
function ShowTitle({ title }: { title: string }) {
return (
<>
<h2>{title}</h2>
</>
);
}
列表
todoList
是reduce中管理的所有事项的列表。done
表示当前列表是代办(false)还是已完成(true)handleToggleTodo
是点击复选框更改列表项状态的方法
// 展示todo的列表
function ShowList({
todoList,
done,
handleToggleTodo,
}: {
todoList: Array<Todo>;
done: boolean;
handleToggleTodo: Function;
}) {
const data = todoList.filter((item) => {
return item.done === done;
});
const show = data.map((item) => {
return (
<ListItem
key={item.id}
item={item}
handleToggleTodo={handleToggleTodo}
></ListItem>
);
});
return <>{show}</>;
}
列表项
// list的每一项
function ListItem({
item,
handleToggleTodo,
}: {
item: Todo;
handleToggleTodo: Function;
}) {
return (
<div className="item">
<input
type="checkbox"
checked={item.done}
onChange={() => {
handleToggleTodo(item.id);
}}
name=""
id=""
/>
{item.text}
</div>
);
}
Main
function Main() {
// useReducer
const [todoList, dispatch] = useReducer(todoReducer, initTodo);
function handleAddTodo(text: string) {
dispatch({
type: "Add_TODO",
text: text,
});
}
function handleToggleTodo(index: number) {
dispatch({
type: "TOGGLE_TODO",
index: index,
});
}
return (
<>
<Add handleAddTodo={handleAddTodo}></Add>
<ShowTitle title={"Todo"}></ShowTitle>
<ShowList
todoList={todoList}
done={false}
handleToggleTodo={handleToggleTodo}
></ShowList>
<ShowTitle title={"Done"}></ShowTitle>
<ShowList
todoList={todoList}
done={true}
handleToggleTodo={handleToggleTodo}
></ShowList>
</>
);
}
总体代码
import { useState, useReducer } from "react";
// 标题
function ShowTitle({ title }: { title: string }) {
return (
<>
<h2>{title}</h2>
</>
);
}
// 添加todo的输入框
function Add({ handleAddTodo }: { handleAddTodo: Function }) {
const [inputValue, setInputValue] = useState("");
return (
<>
<div className="header">
<input
type="text"
value={inputValue}
onChange={(e) => {
setInputValue(e.target.value);
}}
onKeyDown={(e) => {
if (e.key === "Enter") {
handleAddTodo(inputValue);
setInputValue("");
}
}}
placeholder="在这里添加新的代办事件"
/>
</div>
</>
);
}
// list的每一项
function ListItem({
item,
handleToggleTodo,
}: {
item: Todo;
handleToggleTodo: Function;
}) {
return (
<div className="item">
<input
type="checkbox"
checked={item.done}
onChange={() => {
handleToggleTodo(item.id);
}}
name=""
id=""
/>
{item.text}
</div>
);
}
// 展示todo的列表
function ShowList({
todoList,
done,
handleToggleTodo,
}: {
todoList: Array<Todo>;
done: boolean;
handleToggleTodo: Function;
}) {
const data = todoList.filter((item) => {
return item.done === done;
});
const show = data.map((item) => {
return (
<ListItem
key={item.id}
item={item}
handleToggleTodo={handleToggleTodo}
></ListItem>
);
});
return <>{show}</>;
}
function Main() {
// useReducer
const [todoList, dispatch] = useReducer(todoReducer, initTodo);
function handleAddTodo(text: string) {
dispatch({
type: "Add_TODO",
text: text,
});
}
function handleToggleTodo(index: number) {
dispatch({
type: "TOGGLE_TODO",
index: index,
});
}
return (
<>
<Add handleAddTodo={handleAddTodo}></Add>
<ShowTitle title={"Todo"}></ShowTitle>
<ShowList
todoList={todoList}
done={false}
handleToggleTodo={handleToggleTodo}
></ShowList>
<ShowTitle title={"Done"}></ShowTitle>
<ShowList
todoList={todoList}
done={true}
handleToggleTodo={handleToggleTodo}
></ShowList>
</>
);
}
function App() {
return (
<>
<div className="main">
<Main></Main>
</div>
</>
);
}
interface Todo {
readonly text: string;
readonly done: boolean;
readonly id: number;
}
interface AddTodo {
type: "Add_TODO";
text: string;
}
interface ToggleTodo {
type: "TOGGLE_TODO";
index: number;
}
// 操作只有添加todo和修改todo两种
type TodoAction = AddTodo | ToggleTodo;
// 点击修改状态
function todoReducer(list: Array<Todo>, action: TodoAction): Array<Todo> {
switch (action.type) {
case "Add_TODO": {
return [
...list,
{
text: action.text,
done: false,
id: list.length,
},
];
}
case "TOGGLE_TODO": {
return list.map((item, index) => {
if (index !== action.index) {
return item;
}
return {
text: item.text,
done: !item.done,
id: item.id,
};
});
}
default: {
return list;
}
}
}
// 模拟数据
const initTodo: Array<Todo> = [
{
text: "吃饭",
done: true,
id: 0,
},
{
text: "睡觉",
done: true,
id: 1,
},
{
text: "上班",
done: true,
id: 2,
},
{
text: "下班",
done: false,
id: 3,
},
{
text: "运动",
done: false,
id: 4,
},
{
text: "听歌",
done: false,
id: 5,
},
];
export default App;