如何使用React Hooks的任务跟踪器应用程序

81 阅读11分钟

使用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 组件,以便在那里引用它。

todosTodoList.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 ,完成。

Remove

随着功能的实现,你的应用程序将看起来更好。你将通过在终端运行以下命令来为这个项目使用材料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钩子创建一个任务跟踪器应用,你看到了如何使用状态、效果和传递道具。