【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;
}

对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;

猜你喜欢

转载自blog.csdn.net/karshey/article/details/134312251