使用React Hooks的任务跟踪器应用程序
在我们的日常生活中,我们都有计划在一天的时间结束前完成的任务。通常情况下,我们会使用任务跟踪应用程序来记录我们要做的一切。
在这篇文章中,你将学习如何用React建立一个任务跟踪器,允许你添加、更新和删除任务。你还将学习如何在React中使用钩子,管理状态,以及使用道具。
开始学习
首先,创建一个React应用程序。
设置好你的React应用程序后,继续并创建一个组件文件夹。你的应用程序将由三个主要组件组成。TodoForm.js,TodoList.js, 和Todo.js 。
你的所有功能都将在你的App.js ,它作为你的应用程序的入口点。
注意:在本教程中,我们使用Material UI来进行造型设计。
TodoForm
在TodoForm 组件中,创建一个名为TodoForm() 的函数。TodoForm 组件将被用来创建需要添加到列表中的to-do 的表单。记住要导入React ,并导出TodoForm() 。
在这个TodoForm() 函数中,继续前进并返回一个表单元素,里面有一个输入和一个按钮。在TodoForm() ,导入useState ,因为你需要定义一些状态来跟踪用户的输入。
为了跟踪用户的输入,定义一个名为to-do 的状态。然后setTodo() 作为函数初始化为一个具有三个属性的对象,ID,是一个字符串,任务,也是一个字符串,以及完成,是一个布尔值。
completed 状态将用于跟踪to-do 是否被标记为完成,而任务将描述to-do 。最好是在用户为to-do 输入时定义一个函数,以便在你的状态中跟踪它。
你需要创建一个名为handleTaskInputChange() 的函数,以一个事件为参数。这个函数将负责更新你的to-do 对象上的任务属性。在这个函数中,你将调用setTodo ,传入一个具有旧的to-do 属性的新对象,并用事件目标值的新值来更新任务属性。
在这种情况下,事件目标值包含来自用户的新输入文本。你的返回语句将定义onChange() 事件函数,在每次事件触发时运行你的新handleTaskInputChange() 函数。在这种情况下,onChange() 将在每次输入值变化时触发。
同时,你将输入值设置为todo.task ,因为每次调用handleTaskInputChange() ,输入值就会被更新。然后,你也给你的按钮一个类型,叫做提交。所以,现在你有一些输入数据可以使用,你需要处理当你想在列表中添加一个新的to-do 的情况。
在app.js ,你将定义一个名为todos 的单一状态,一个数组。接下来,你需要创建一个名为addTodo() 的函数,它将接收一个新的to-do ,并将其添加到todos 的数组中。要将to-do 添加到列表中,请调用setTodos() 函数,然后传入一个包含新的to-do 的数组,将其添加到开头,并将旧的todos 数组铺在其上。
你需要把你的新addTodo() 函数作为一个道具传给TodoForm 组件。现在,在你的TodoForm 组件中,你需要从道具参数,即第一个道具中解构addTodo() 函数。
当用户提交表单时,你需要将表单to-do 从状态中添加到列表中。为了做到这一点,你将创建一个handleSubmit() 函数,该函数也从DOM中接收一个事件。你需要调用preventDefault() 函数来防止默认浏览器自动提交。
然后你需要写一个if语句,只有当to-do.task 不是空的时候才会被调用。你将通过在你的to-do.task.trim() 上调用trim() 函数来做到这一点,它将删除字符串中的空白。然后在if语句中,你需要用一个带有to-do 传播的对象调用你的新addTodo() 函数,并更新ID属性。你将从一个UUID包中得到这个ID,它将为我们生成一个ID。
要安装这个,打开控制台,输入yarn add UUID或npm add UUID,然后在你的TodoForm 文件中,导入UUID并调用v4来生成to-do ID。然后在你添加了to-do ,你要通过调用setTodo ,用一个新的对象来重置表单,这个对象有旧的属性铺在上面,然后用一个空字符串来更新任务属性。
现在你可以通过定义onSubmit 属性作为新的handleSubmit() 函数来使用这个handleSubmit() 函数,并在表单被提交时启动它。
import { Button, TextField } from "@material-ui/core";
import React, { useState } from "react";
import uuid from "uuid";
function TodoForm({ addTodo }) {
const [to-do, setTodo] = useState({
id: "",
task: "",
completed: false
});
function handleTaskInputChange(e) {
// e.target.value contains new input from onChange
// event for input elements
setTodo({ ...to-do, task: e.target.value });
}
function handleSubmit(e) { e.preventDefault();
// prevents browser refresh
// trim() gets rid of string whitespace
if (to-do.task.trim()) {
addTodo({ ...to-do, id: uuid.v4() });
// reset task input
setTodo({ ...to-do, task: "" });
}
}
return (
<form className="todo-form" onSubmit={handleSubmit}>
<TextField
label="Task"
type="text"
name="task"
value={to-do.task}
onChange={handleTaskInputChange}
/>
<Button type="submit">Submit</Button>
</form>
);
}
export default TodoForm;
TodoList
TodoList 将负责在一个数组中呈现 的列表。使用简短的表单,导入 react 并导出 。你需要从组件道具中解构 ,然后在返回语句中渲染一个无序的列表。todos TodoList todos
在这个无序列表中,你需要通过在大括号中插入一些JavaScript并使用map数组函数来映射到todos 。然后你需要返回你的Todo 组件,在你的map里面把to-do 对象作为一个道具传进去。
请注意,当在数组地图中渲染JSX元素时,每个项目都应该有一个唯一的键连接到从地图返回的父元素。
你需要做的下一件事是定义你的Todo 组件的外观。
import { List } from "@material-ui/core";
import React from "react";
import Todo from "./Todo";
function TodoList({ todos, removeTodo, toggleComplete }) {
return (
<ul>
{todos.map(to-do => (
<Todo
key={to-do.id}
to-do={to-do}
removeTodo={removeTodo}
toggleComplete={toggleComplete} />
))}
</ul>
);
}
export default TodoList;
Todo
Todo 将从列表中渲染一个 。你的 ,需要有三个主要元素,复选框,任务,和删除按钮。使用简短的表格,导入 react 并导出 。to-do Todo Todo.js
在你的返回语句中,你需要创建一个输入:一个带有项目列表的复选框,有to-do.task ,一个按钮都在一个div里面。接下来,你将通过指定包含样式类型的自定义camel-case属性的样式道具来附加自定义样式。
然后,你将给这个div一个flex的显示方式,以使元素在水平方向上彼此相邻。另外,对于列表样式,你需要通过使用三元表达式,在to-do 得到完成时,给文本装饰样式一个line-through的值。
如果你运行你写的代码,你可以看到你可以在你的列表中添加一个to-do ,而且每次你提交表单时你的用户界面都会更新。然而,如果你刷新页面,你的所有Todo ,就会丢失。因此,你需要利用本地浏览器的存储,这样你的状态就不会被重置。
在App.js ,你需要导入一个叫做useEffect() 的函数。useEffect 是一个方便的钩子,提供响应你代码中特定数据或函数的功能。每当你的todos 阵列发生变化时,你要把这些新的数据存储在本地存储里面。
要做到这一点,你需要定义一个效果,这个效果接收一个函数和一个依赖数组,里面有一个todos 状态。接下来,你需要定义一个唯一的本地存储键,你可以用它来存储todos 。
为了这个效果,你需要在全局-本地存储变量上调用setItem() 函数,该变量提供了你自定义的本地存储键和todos 数组stringify与JSON.stringify() 。接下来,你要在应用程序渲染时添加todos 。
要做到这一点,你将定义一个带有空依赖数组的useEffect 。在效果内部,你需要从本地存储中获取todos ,并通过使用LOCAL_STORAGE_KEY ,在本地存储中调用getItem ,将它们存储在一个变量中。然后使用JSON.parse ,将从中返回的字符串传递到JSON 。
现在,你需要在这个值上调用setTodos ,但只有在空时才调用。如果你再次执行该应用程序,你会看到在刷新浏览器时,Todos 不会消失。相反,你需要对实际的to-do,toggleComplete, 和delete 进行操作。
回到App.js ,你需要在addTodo 下创建一个新的函数,叫做toggleComplete() ,它接收一个to-do 的ID。然后,为了更新to-do ,你需要调用setTodos ,你需要传递一个新的todos 数组,你将通过执行映射得到这个数组。
你映射每个to-do ,检查该to-do 的ID是否与传入的ID一致。如果是的话,你需要返回一个新的对象,该对象具有完成的属性。这将导致在一个特定的ID上运行该函数时,false变成true,true变成false。你拿着这个函数并把它传递给TodoList 组件,以便在那里引用它。
todos 在TodoList.js ,你可以从属性中解构togglecomplete() 函数,并通过再次解析它来映射到你的Todo 组件。例如,你想让togglecomplete() 函数在点击to-do 复选框时启动。
要做到这一点,你需要在Todo 组件内创建一个名为handlecheckboxclick() 的函数,它将用to-do 的ID调用togglecomplete() 。然后你需要填写复选框输入onClick 属性,以使用你的handleTodoClick() 函数。
回到App.js ,你现在可以在删除一个to-do 。首先,你将创建一个removeTodo() 函数,它从to-do 。在这个函数中,你需要用一个新的todos 数组传递给removeTodo ,调用setTodos 。
幸运的是,有一个方便的数组原型函数,叫做Filter ,它对于从数组中删除项目非常好。Filter 需要一个函数,然后它将用这个函数来决定是否应该保留数组中的一个元素。
在这种情况下,如果ID不是你要找的那个,你要保留to-do ;否则,从列表中删除to-do 。这个Filter的返回值是另一个todos ,其ID与从数组中删除的参数相同,to-do 。像以前一样,你要把这个新函数作为一个道具传给TodoList ,这样每个to-do 都可以访问它。
把这个新的属性解构到TodoList ,并把它交给Todo 组件。现在在Todo 组件中,你可以从道具中抓取这个函数,并将其用于你的删除按钮。像以前一样,你需要创建一个名为handleRemoveclick() 的函数,它将用to-do.ID 来调用removeTodo 。现在你可以将这个函数作为onClick 的功能,用于to-do 删除按钮。
如果你运行该应用程序,你现在可以添加、删除和切换todos ,完成。
![]()
随着功能的实现,你的应用程序将看起来更好。你将通过在终端运行以下命令来为这个项目使用材料UI。
# install material UI with npm
npm install @material-ui/core
#install material UI with yarn
yarn add @material-ui/core
#install material UI with npm
npm install @material-ui/icons
#install material UI with yarn
yarn add @material-ui/icons
在App.js ,你需要删除标题元素,并添加material UI的排版组件。然后将你的段落标签改为排版组件,并将其变体设置为h1 ,并添加一些padding。
在TodoForm ,从material UI导入一个按钮和文本字段,用textField 替换你的输入,并添加一个标签属性。然后用material UI的按钮代替普通按钮。
然后你会给表单一个className ,你可以对其进行样式设计。然后在TodoList ,你需要用一个材料UI的列表组件来替换UL 标签。在Todo 组件中,导入图标按钮、复选框、列表项、关闭图标和材料UI的排版。然后用一个排版组件替换常规的列表项元素,并将变量设置为body1 。
用一个复选框组件替换输入元素,并给它一个与to-do.Completed 属性相对应的复选属性和一个调用复选框点击的onClick() 函数。另外,用一个图标按钮代替删除按钮,并将关闭图标放在里面。
现在,你需要给App.CSS 添加一些样式,把所有东西都对准中心,把元素的高度设置为100%,并把TodoForm 元素垂直居中。
如果你回到你的浏览器,你会看到你的应用程序处于完成状态。
import { Checkbox, IconButton, ListItem, Typography } from "@material-ui/core"; import CloseIcon from "@material-ui/icons/Close";import React from "react";
function Todo({ to-do, toggleComplete, removeTodo }) {
function handleCheckboxClick() { toggleComplete(to-do.id);
}
function handleRemoveClick() {
removeTodo(to-do.id);
}
return (
<ListItem style={{ display: "flex" }}> <Checkbox checked={to=do.completed} onClick={handleCheckboxClick} /> <Typography
variant="body1"
style={{
textDecoration: to-do.completed ? "line-through" : null
}}
>
{to-do.task}
</Typography>
<IconButton onClick={handleRemoveClick}> <CloseIcon />
</IconButton>
</ListItem>
);
}
export default Todo;
结论
这篇文章是人们开始使用React的一个垫脚石。你学会了如何用React钩子创建一个任务跟踪器应用,你看到了如何使用状态、效果和传递道具。