用react hooks构建一个todoList

栏目: 服务器 · 发布时间: 4年前

内容简介:本文翻译来自于pc版本预览构建一个简单的todoList用react和react hooks.这是一个很好的教程对于初学者和中级开发人员.

本文翻译来自于 upmostly.com/tutorials/b…

pc版本预览

用react hooks构建一个todoList

构建一个简单的todoList用react和react hooks.这是一个很好的教程对于初学者和中级开发人员.

我将带你走进如何用react构建简单的todo list,仅仅使用functional组件和新的useState react hook

tips: useState hook 将使我们能够存储状态的内部功能组件。:wave:Goodbye过于混乱 类组件,你好hook!:fishing_pole_and_fish:

创建一个新react项目

我们将跳过所有手动构建配置过程,这样我们能够快速进入正题

npx create-react-app todo-app
复制代码

然后我们用ide开发todo-app文件夹

写html,css样式

用react hooks构建一个todoList

当我创建一个新的react 组件,我喜欢首先编写的HTML和CSS。 我们通常不会在内部类组件中添加render方法,相反,我们直接返回HTML功能组件 替换app.js成下面的代码

App.js
import React from 'react';
import logo from './logo.svg';
import './App.css';

function App() {
  return (
    <div className="app">
      <div className="header">
        <img src={logo} className="logo" alt="logo" />
      </div>
      <form className="todo-list">
        <ul>
          <div className="todo">
            <div className="checkbox" />
            <input type="text" value="Todo one" />
          </div>
        </ul>
      </form>
    </div>
  );
}

export default App;
复制代码

粘贴下面的CSS到 App.css里面,然后你也可以修改它,修改成你喜欢的样子。

App.css
body {
  background-color: #282c34;
  min-height: 100vh;
}

.app {
  padding-top: 10rem;
}

.header {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}

.logo {
  animation: App-logo-spin infinite 20s linear;
  height: 20vmin;
  pointer-events: none;
}

.todo-list {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  color: white;
}

.todo {
  display: flex;
  align-items: center;
  margin: 1rem 0;
}

.todo-is-completed .checkbox {
  color: #000;
  background: #fff;
}

.todo-is-completed input {
  text-decoration: line-through;
}

.checkbox {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  margin-right: 10px;
  cursor: pointer;
  font-size: 10px;
  display: flex;
  justify-content: center;
  align-items: center;
  transition: background-color .2s ease-in-out;
  border: 1px solid #fff;
}

.checkbox:hover {
  background: rgba(255, 255, 255, 0.25);
  border: 1px solid rgba(255, 255, 255, 0);
}

.checkbox-checked {
  color: #000;
  background: #fff;
}

ul {
  list-style: none;
  padding: 0;
  line-height: 2rem;
  width: 500px;
}

input {
  border: none;
  background: transparent;
  color: white;
  font-size: 1.4rem;
  outline: none;
  width: 100%;
}

@keyframes App-logo-spin {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}
复制代码

用useState Hook添加State

现在,我们有一个漂亮的todo应用,让我们开始hook。

为什么我们需要State?

State允许我们来跟踪变化在React组件中。一个todo列表经常变化。例如:

  • 添加新的待办事项
  • 改变现有待办事项
  • 删除待办事项
  • 完成待办事项
  • 没有完成待办事项 接下来在App.js文件的顶部 import useState hook
import React, { useState } from 'react';
复制代码

终于我们可以初始化我们组件的state属性,像下面这个样子

App.js
...

function App() {
  const [todos, setTodos] = useState([
    {
      content: 'Pickup dry cleaning',
      isCompleted: true,
    },
    {
      content: 'Get haircut',
      isCompleted: false,
    },
    {
      content: 'Build a todo app in React',
      isCompleted: false,
    }
  ]);
  
...
复制代码

tips: 记住,hook 必须初始化react 组件的内部。你不能在函数外部初始化它。

当你使用useState hook ,将会添加两个值:getter和setter。在上面的代码中,todos是state本身,setTodos 是更新state 值的方法。 我们初始化todos设置一个默认对象,这个对象用一个数组填充。为什么我们使用对象而不是简单的字符串?因为我们需要存储为每个做两条信息:

  • 待办事项的内容
  • 待办事项是否已经完成

显示待办事项

保存项目然后跳转到你的应用程序在浏览器中运行的情况(执行npm start),你将看到一条todo item.. 这么奇怪。我们有三个待办事件在我们的State,为什么我们只看到一条。 我们设置状态初始值,但还没有返回语句todo状态值 显示待办事项,我们需要循环todos数组和呈现一个todo项数组里面的每一项行动计划。为此,我们将使用map函数

...
<form className="todo-list">
  <ul>
    {todos.map((todo, i) => (
      <div className="todo">
        <div className="checkbox" />
        <input type="text" value={todo.content} />
      </div>
    ))}
  </ul>
</form>
...
复制代码

map是一个很常见的函数。你可能会遇到很多次,所以重要的是你需要了解它是如何工作的。 上面的代码是遍历todos数组然后渲染括号内HTML为数组中的每一项。 然后保存一下代码,你将看到。。。

用react hooks构建一个todoList

新建一个新的待办事件

现在我们能够显示待办事项列表,让我们添加新建一个todo项的能力。 首先添加一个onKeyDown事件处理程序的输入字段:

...

<div className="todo">
  <div className="checkbox" />
  <input
    type="text"
    value={todo.content}
    onKeyDown={e => handleKeyDown(e, i)}
  />
</div>
...
复制代码

onKeyDown调用一个名为handleKeyDown的函数。通过输入的名称以及待办事项的索引。handleKeyDown里面,我们发现如果返回键被按下。如果是,我们称之为createTodoAtIndex。让我们添加这两个函数在返回语句,如下所示:

...

function handleKeyDown(e, i) {
  if (e.key === 'Enter') {
    createTodoAtIndex(e, i);
  }
}

function createTodoAtIndex(e, i) {
  const newTodos = [...todos];
  newTodos.splice(i + 1, 0, {
    content: '',
    isCompleted: false,
  });
  setTodos(newTodos);
  setTimeout(() => {
    document.forms[0].elements[i + 1].focus();
  }, 0);
}
...
复制代码

createTodoAtIndex函数看起来很复杂,但实际上非常简单。让我将其分解:

  • 我们通过监听返回键被按下然后检查event.key的值
  • 接下来,我们创建一个副本todos状态数组。我们这样做是因为 state不应该直接修改
  • 使用复制出来的待办事件,我们插入一个新的空待办事项后当前选中待办事项。这就是为什么我们需要在当前todo 的index传递给这个函数
  • 插入新的todos副本后,我们更新原始数组的副本
  • 最后,我们将focus到Input中

您可能已经注意到,代码行focus到Input中超时触发后0毫秒 更新内部状态的组件不发生瞬间反应。有时候需要时间,尤其是我们更新包含大量的数据。 因此我们添加一个延时去等待state完成更新之后focusing on新渲染的input。 [站外图片上传中...(image-7255ee-1561032624160)]

更新一个待办事项

现在我们可以创建一个新的任务,让我们添加一个实际上填入的值。 文本框有一个onChange事件该字段的值改变时触发。 小心,虽然input values本身并不提供更新绑定函数,相反,一个event对象允许您通过event.target.value找到更新后的values 在createTodoAtIndex方法之后 添加以下方法

...

function updateTodoAtIndex(e, i) {
  const newTodos = [...todos];
  newTodos[i].content = e.target.value;
  setTodos(newTodos);
}

...
复制代码

很像createTodoAtIndex方法,updateTodoAtIndex两个参数:输入event和待办事项index,同样的,我们复制todos数组来避免直接state. 在这个复制出来的数组中,我们给todos添加content key,内容是event 中的values值。最后,我们用复制出来的数组更新todos的state [站外图片上传中...(image-2065b1-1561032624160)]

删除todos

下面我们来实现,todos values内容为空之后输入退格键,删除这个todos的功能。 我们称之为一个叫做removeTodoAtIndex新功能

function handleKeyDown(e, i) {
  if (e.key === 'Enter') {
    createTodoAtIndex(e, i);
  }
  if (e.key === 'Backspace' && todos[i].content === '') {
    e.preventDefault();
    return removeTodoAtIndex(i);
  }
}

function removeTodoAtIndex(i) {
  if (i === 0 && todos.length === 1) return;
  setTodos(todos => todos.slice(0, i).concat(todos.slice(i + 1, todos.length)));
  setTimeout(() => {
    document.forms[0].elements[i - 1].focus();
  }, 0);
}
复制代码

保存组件并返回到您的浏览器您就可以自己试一下具体的功能

完善todos

我们讨论了创建、更新和删除一个todo。现在让我们来完善它。 现在,我给圆圈添加了hover效果,不过点击并没有任何效果。 让我们改变它 添加一个onClick绑定事件以及todo-is-completed样式名

...

<div className={`todo ${todo.isCompleted && 'todo-is-completed'}`}>
  <div className={'checkbox'} onClick={() => toggleTodoCompleteAtIndex(i)}>
    {todo.isCompleted && (
      <span>✔</span>
    )}
  </div>
  <input
    type="text"
    value={todo.content}
    onKeyDown={e => handleKeyDown(e, i)}
    onChange={e => updateTodoAtIndex(e, i)}
  />
</div>

...
复制代码

最后,添加toggleTodoCompletedAtIndex函数

function toggleTodoCompleteAtIndex(index) {
  const temporaryTodos = [...todos];
  temporaryTodos[index].isCompleted = !temporaryTodos[index].isCompleted;
  setTodos(temporaryTodos);
}
复制代码

保存之后,让我们看下最后的成果吧! [站外图片上传中...(image-93efad-1561032624160)]

完成的源码

下面我提供了完整的源代码,这样你就可以看到我们的反应todo组件在其所有的使用方式。

import React, { useState } from 'react';
import logo from './logo.svg';
import './App.css';

function App() {
  const [todos, setTodos] = useState([
    {
      content: 'Pickup dry cleaning',
      isCompleted: true,
    },
    {
      content: 'Get haircut',
      isCompleted: false,
    },
    {
      content: 'Build a todo app in React',
      isCompleted: false,
    }
  ]);

  function handleKeyDown(e, i) {
    if (e.key === 'Enter') {
      createTodoAtIndex(e, i);
    }
    if (e.key === 'Backspace' && todos[i].content === '') {
      e.preventDefault();
      return removeTodoAtIndex(i);
    }
  }

  function createTodoAtIndex(e, i) {
    const newTodos = [...todos];
    newTodos.splice(i + 1, 0, {
      content: '',
      isCompleted: false,
    });
    setTodos(newTodos);
    setTimeout(() => {
      document.forms[0].elements[i + 1].focus();
    }, 0);
  }

  function updateTodoAtIndex(e, i) {
    const newTodos = [...todos];
    newTodos[i].content = e.target.value;
    setTodos(newTodos);
  }

  function removeTodoAtIndex(i) {
    if (i === 0 && todos.length === 1) return;
    setTodos(todos => todos.slice(0, i).concat(todos.slice(i + 1, todos.length)));
    setTimeout(() => {
      document.forms[0].elements[i - 1].focus();
    }, 0);
  }

  function toggleTodoCompleteAtIndex(index) {
    const temporaryTodos = [...todos];
    temporaryTodos[index].isCompleted = !temporaryTodos[index].isCompleted;
    setTodos(temporaryTodos);
  }

  return (
    <div className="app">
      <div className="header">
        <img src={logo} className="logo" alt="logo" />
      </div>
      <form className="todo-list">
        <ul>
          {todos.map((todo, i) => (
            <div className={`todo ${todo.isCompleted && 'todo-is-completed'}`}>
              <div className={'checkbox'} onClick={() => toggleTodoCompleteAtIndex(i)}>
                {todo.isCompleted && (
                  <span>✔</span>
                )}
              </div>
              <input
                type="text"
                value={todo.content}
                onKeyDown={e => handleKeyDown(e, i)}
                onChange={e => updateTodoAtIndex(e, i)}
              />
            </div>
          ))}
        </ul>
      </form>
    </div>
  );
}

export default App;
复制代码

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

PHP for the World Wide Web, Second Edition (Visual QuickStart Gu

PHP for the World Wide Web, Second Edition (Visual QuickStart Gu

Larry Ullman / Peachpit Press / 2004-02-02 / USD 29.99

So you know HTML, even JavaScript, but the idea of learning an actual programming language like PHP terrifies you? Well, stop quaking and get going with this easy task-based guide! Aimed at beginning ......一起来看看 《PHP for the World Wide Web, Second Edition (Visual QuickStart Gu》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

在线进制转换器
在线进制转换器

各进制数互转换器