上节课我们介绍了如何在脚手架中如何对props进行限制。这节课我们来学习如何删除一个todo

回顾

在开始学习这节课的内容之前我们先俩对上节课的内容来做一个简单的回顾:

  • prop-types需要手动安装
  • prop-types不是必要的,但是加了可以更加规范,方便维护

以上便是上节课的主要要点,其实并没有什么太重要的东西。接下来我们开始这节课的内容。

删除todo的需求场景

我们之前已经做了添加与完成包括取消完成个这写功能。但是我们想一想如果我一个todo添加了,但是突然接到通知说这件事不需要做了,那么我们是不是要删除这个todo

或者说我们们在规划一天要做的事情,所有的都规划好了,但是中途有事耽搁了一下,然后发现,我们有些事情没有时间做了,那么这些我们是不是也要删掉?

再或者我们一天的事情太多了,而且我们也完成了很多事情,然后每次在看我们的整个清单的时候,完成的和未完成的都夹杂在一起就不够直观。那么我们是不是要删掉这些已完成的事情?这就是我们的需求场景

功能分析

image-20220110130849892

首先我们先来看一下我们的效果图,上面这个清单当我鼠标移入,是不是当前的todo就高亮了?而且还显示了一个删除按钮。那么我们要删除这个todo是不是就要给这个删除按钮加上onClick事件?

那么我们再来分析一下,我们页面上面展示出来的这个todo的列表是怎么来的?是不是遍历了App组件的state的值?那么我们删除按钮的onClick事件是不是要更改App组件的state

以上就是我们删除todo功能的一个实现流程好我们来提两个问题:

  • 删除按钮在哪个组件中?
  • 如修改state

第一,我们都知道这个删除按钮是Item组件中的一个button标签,所以我们是不是要给button标签添加onClick事件?

第二我们怎么修改state?这里问的怎么修改state可不是在问我们应该怎么样去拿到App组件的state然后做修改,这个问题我们已经讲了好几遍了。再老是唠叨这个问题就没有意思了。那么这里的问题是什么意思呢?其实是在问,我们修改的思路,我们根据什么修改?第一我们是不是要拿到这个todoid?只有知道了id我们才可以去Appstate中去匹配对应的todo对象啊。

那么怎么拿?我们onClick事件是给谁加的?是不是给<button>?那么这个id在哪?是不是在props里?所以说拿到id简单。那么紧接着是不是就要传给App组件提供的函数中,因为要改父组件的state必须要由父组件提供入口函数啊。然后在入口函数接收到了id之后是不是就要作出相应的处理来修改state

以上就是我们删除todo功能的实现思路。

实现

我们上面已经分析了思路了,那么是不是就简单了啊?我们来看一下代码

export default class Item extends Component {
  state = { mouse: false };

  render() {
    const { todo } = this.props;
    const { mouse } = this.state;
    return (
     ...
        <button onClick={this.handleDelete(todo.id)} ...>删除</button>
      ...
    )
  }

  handleCheck = id => {...};
  handleDelete = id => {
    return () => {
      console.log(id);
    };
  };
  handleMouse = flag => {...};
}

一些目前不需要关注的代码我就省略了,我们但看<button>,我们给绑定了onClick事件,设置了回调是handleDelete方法,并且从props中拿到了当前todoid传给了handleDelete方法。再来看看这个方法,我们只需要接收id参数,但是我们在<button>中是不是直接调用了这个方法?那么我们就要返回一个函数。那么我们来看一下效果:

image-20220110132916263

我们点击按钮是不是就能成功打印出id了啊?但是我们的目的又不是打印id,那么我们是不是要在App组件中来提供一个给Item组件来修改state的入口函数?我们来看一下:

export default class App extends Component {

  state = { todos: [] };

  add = dataObj => {...};
  update = (id, done) => {...}
  deleteTodo = id => {
    const { todos } = this.state;
    const newTodos = todos.filter(todo => todo.id !== id);
    this.setState({ todos: newTodos });
  }

  render() {
    const { todos } = this.state;
    return (
      ...
          <List todos={todos} update={this.update} deleteTodo={this.deleteTodo} />
      ...
     )
  }
}

我们来看首先我们来看deleteTodo方法,这个就是用来删除的方法。首先我们是不是要先获取我们之前的todos?那么我们怎么通过一个id来从数组中删掉对应的todo对象呢?如果这里大家们,没有在第一时间想到数组的filter方法,那么赶紧回去复习原生js中的数组那一块的内容。

filter方法是要接收一个执行器函数来做处理的,那么这个函数怎么写?我们要过滤掉某个id的元素,是不是直接返回那些和指定id不等的元素就可以了?

好了,现在数组也处理好了,啊么是不是就修改state就行了,那么这个方法就算是完成了,接下来我们要把这个方法提供给Item组件使用,但是Item不是App的子组件啊,这个我们上节课是不是就已经说过了?ItemList的子组件,而ListApp的子组件,那么就把方法传给List然后在List中结构赋值拿到函数再传给Item。我们来看一下代码

// List 组件
export default class List extends Component {
  render() {
    const { todos, update, deleteTodo } = this.props;
    return (
      <ul className="todo-list">
        {todos.map(todo => <Item ... deleteTodo={deleteTodo} />)}
      </ul>
    )
  }
}
// Item 组件
export default class Item extends Component {
  state = { mouse: false };

  render() {...}

  handleCheck = id => {...};
  handleDelete = id => {
    return () => {
      const {deleteTodo} = this.props;
      deleteTodo(id);
    };
  };
  handleMouse = flag => {...};
}

List组件中拿到deleteTodo函数并将该函数传给了Item组件,在ItemhandleDelete方法中直接调用deleteTodo方法即可。那么我们来看一下效果是不是符合我们预期:

image-20220110140822001

之前我们是这些todo那么我们删除来看一下:

image-20220110140919205

删除之后便是只剩下这两个todo。所以至此我们的删除todo功能就已经完成了。

可能有些人会问,为什么我们之前都是updateadd这次的方法用了deleteTodo?因为如果我们直接写delete作为方法名的话,在List中解构赋值那一步就会报错,为什么?因为delete是一个关键字,用于删除对象中的指定属性,问出这个问题的也要去复习一下原生js的基础语法了。

弹窗确认

现在有一个问题如果我不小心误删了怎么办?我们可没有回收站功能啊,那么我们加一个弹窗提醒呗,就在Item组件的handleDelete方法中加一个是不是就可以了?但是我们能用alert吗?不能啊,alert只有一个确定按钮啊,这不是绑架嘛,让别人只能删除。那么我们原生js中是不是有一个confirm啊?点击确定就返回true否则返回false

image-20220110142849169

但是有意思的来了报错了,react不认识这个confirm,那么怎么办呢?我们需要告诉react我们用的是window.confirm。那么我们来看一下

export default class Item extends Component {
  state = { mouse: false };

  render() {...}

  handleCheck = id => {...};
  handleDelete = id => {
    return () => {
      if (!window.confirm("Are you sure you want to delete?")) return;
      const {deleteTodo} = this.props;
      deleteTodo(id);
    };
  };
  handleMouse = flag => {...};
}

我们添加了这一行,如果confirm返回的是false,我们就不做任何操作,否则删除。来看一下效果:

image-20220110143253790

image-20220110143306436

点击确定删除喝茶todo,那么如果我们点击取消,睡觉todo没有被删除。

总结

  • 数组的filter方法用来根据某些条件完成过滤效果
  • delete是一个关键字,不能直接拿来做方法名
  • confirm再使用时要指定window.confirm

Copyright statement:The articles of this site are all original if there is no special explanation, indicate the source please when you reprint.

Link of this article:https://work.lynchow.com/article/todolist_delete_todo/