setState() - удаление элемента


В предыдущей части мы сумели передать событие клика с кнопки элемента TodoListItem в компонент App.
В  App находится массив todoDate, на основе которого формируется список дел.
Но простое удаление элемента из массива не вариант - после удаления элемента реакт не будет знать, что приложению следует обновиться.
Нам нужно сделать массив todoDate частью состояния App и удалять его при помощи метода setState(). Использование этого метода даёт реакту чигнал о том, что произошли какие-то изменения и приложение нужно обновить.

Преобразуем App в класс и сделаем todoDate частью его состояния.

Было:

import React from 'react';

const App = () => {
   const todoDate = [
    {label: 'Learn JS', important: true, id: 1},
    {label: 'Create awesome app', important: true, id: 2},
    {label: 'Drink coffee', important: false, id: 3}
  ]; 
  
   return ( 
     <div className="todo-app">
      <AppHeader/>
      <SearchPanel/>
      <TodoList todos={todoDate}
       onDelete = {(id) => console.log('Delete' + id)}/>
    </div>
  );
};

export default App;

Стало:

import React, {Component} from 'react';

export default class App extends Component {
  state = {
    todoDate: [
      {label: 'Learn JS', important: true, id: 1},
      {label: 'Create awesome app', important: true, id: 2},
      {label: 'Drink coffee', important: false, id: 3}
    ]
  }
  render() {
    return ( 
     <div className="todo-app">
      <AppHeader/>
      <SearchPanel/>
      <TodoList todos={this.state.todoDate}
       onDelete = {(id) => console.log('Delete' + id)}/>
    </div>
    );
  }
}  

Перепишем свойство onDelete, добавив ему функцию deleteItem. Для начала просто убедимся, что она работает

export default class App extends Component {
  state = {
    todoDate: [
      {label: 'Learn JS', important: true, id: 1},
      {label: 'Create awesome app', important: true, id: 2},
      {label: 'Drink coffee', important: false, id: 3}
    ]
  };
  
  deleteItem = (id) => {
    console.log('Delete' + id);
  }
  
  render() {
    return ( 
     <div className="todo-app">
      <AppHeader/>
      <SearchPanel/>
      <TodoList todos={this.state.todoDate}
       onDelete = {this.deleteItem} />
    </div>
    );
  }
};

Перепишем функцию deleteItem так, чтобы она не просто выводила сообщение в консоль, но и удаляла элемент массива

  deleteItem = (id) => {
      this.setState(({todoDate}) => {
        const index = todoDate.findIndex(element => element.id === id);
        todoDate.splice(index, 1);
        return {
          todoDate: todoDate
        }
      })
    };

Такой код работает. Но напрямую изменять state это очень плохая практика, так делать нельзя.
В чём здесь проблема.
Метод splice() изменяет существующий массив, заменяя его на новый.
Нам нужен метод, который не заменяет массив, а только изменяет его содержимое.
Новый код функции выглядит так

  deleteItem = (id) => {
      this.setState(({todoDate}) => {
        const index = todoDate.findIndex(element => element.id === id);
        const before = todoDate.slice(0, index);
        const after = todoDate.slice(index+1);
        const newArray = [...before, ...after];
        return {
          todoDate: newArray
        }
      })
    };

Вывод. Нельзя изменять существующий state.

То же самое по пунктам

1. setState() не должен изменять текущий state
2. Методы, которые изменяют массив использовать нельзя
3. Если нужно удалить элемент из массива, 
newArr = [...oldArr.slice(0, index)
          ...oldArr.slice(index + 1)];