• 【React入门实战】实现Todo代办


    非常简单入门的react-todo练习,代码写的很小白。

    效果

    技术栈:react-typeScript

    • 数据分为代办Todo已办完Done,可以点击复选框切换状态
    • 可以添加代办事件
    • 会用到useStateuseReducer,详情看状态管理 – React 中文文档

    在这里插入图片描述
    接下来是代码实现。

    功能-状态管理

    相关参考:
    TypeScript中的标记联合类型:实现Todo代办-CSDN博客
    迁移状态逻辑至 Reducer 中 – React 中文文档 (docschina.org)

    相关接口定义

    Todo的状态表示:

    text表示代办内容,done表示是否完成,id表示它在列表中的位置。

    interface Todo {
    	readonly text: string;
    	readonly done: boolean;
    	readonly id: number;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    对Todo的操作表示:

    interface AddTodo {
    	type: "Add_TODO";
    	text: string;
    }
    
    interface ToggleTodo {
    	type: "TOGGLE_TODO";
    	index: number;
    }
    
    // 操作只有添加todo和修改todo两种
    type TodoAction = AddTodo | ToggleTodo;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    相关方法定义

    操作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;
    		}
    	}
    }
    
    • 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

    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,
    	});
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    模拟的数据: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,
    	},
    ];
    
    • 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

    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>
    		</>
    	);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    标题

    // 标题
    function ShowTitle({ title }: { title: string }) {
    	return (
    		<>
    			<h2>{title}</h2>
    		</>
    	);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    列表

    • 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}</>;
    }
    
    • 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

    列表项

    // 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>
    	);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    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>
    		</>
    	);
    }
    
    • 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

    总体代码

    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;
    
    
    • 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
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
  • 相关阅读:
    【C语言】深入浅出的概述运算符相关知识(详细讲解+源码展示)
    React基础(超级详细,有案例)
    【SA8295P 源码分析】107 - AIS Camera 美信max96712解串器 - max9295加串器 寄存器初始化及工作过程详解
    【python手写算法】正则化在线性回归和逻辑回归中的应用
    离散化(保序)
    单臂路由学习
    前端架构: 脚手架通用框架封装之入口文件开发(教程一)
    离职前一定要做好这7件事情,少一件都很麻烦。
    医学影像SAM
    聊一聊 HBase 是如何写入数据的?
  • 原文地址:https://blog.csdn.net/karshey/article/details/134312251