Собственная система событий


При клике по кнопке с значком корзины будем удалять пункт списка.
Здесь есть проблема: массив, на основе которого формируется список дел, находится в верхнем компоненте приложения App, а кнопка, при клике по которой должен удаляться элемент массива, находится в компоненте TodoListItem, который, в свою очередь, вложен в компонент TodoList. То есть нам нужно как-то передать событие клика из компонента TodoListItem в TodoList, а оттуда в App.

Давайте вспоминать.
Когда нам нужно было передать элементу списка событие клик, мы добавили ему свойство onClick в которое передали функцию.

export default class TodoListItem extends Component {
  render() {
    const { label, important = false } = this.props;
      return (
        <span 

          className="todo-list-item-label"
          onClick = {() => console.log(`${label}`)}>
          {label}
        </span>      
     );
  };
}

Здесь поступаем точно так же, за исключением того, что вместо стандартного свойства onClick используем кастомное свойство onDelete

Поднимаемся в TodoList и в свойства TodoListItem добавляем функцию

 onDelete = {() => console.log('Delete')}

Пока всё, что она делает, просто выводит сообщение в консоль

const TodoList = ({todos}) => {
  const elements = todos.map(item => {
    const {id, ...itemProps} = item;
    return (
      <li key={id} className="list-group-item">
        <TodoListItem {...itemProps}
        onDelete = {() => console.log('Delete')}/>
      </li>
      );
  });
  
  return (
    <ul className="list-group todo-list">
      {elements}
    </ul>
  );
}

Передадим событие onDelete кнопке, которая находится в компоненте TodoListItem 

      <button type="button"
              className="btn btn-outline-danger btn-sm float-right"
              onClick = {this.props.onDelete}>
        <i className="fa fa-trash-o" />
      </button>

Теперь по клику по кнопке в консоли появляется уведомление. То есть нам удалось передать событие из компонента TodoListItem  в компонент TodoList.
Следующий шаг - передать событие из TodoList в App. при этом нам нужно не только указать, что произошёл клике по кнопке, но и на каком элементе он произошёл. Так как у каждого элемента списка есть собственный id, сделать это не сложно.

Поднимаемся вверх в компонент App и добавляем в TodoList, который в нём указан, свойство onDelete, присваивая ему функцию

onDelete = {(id) => console.log('Delete' + id)}

Код полностью:

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>
  );
};

Теперь переходим к компоненту TodoList и меняем свойство onDelete, которому раньше присвоили функцию, выводящую сообщение в консоль. Сейчас это свойство равно

onDelete = {() => onDelete(id)}

Код полностью:

const TodoList = ({todos, onDelete}) => {
  const elements = todos.map(item => {
    const {id, ...itemProps} = item;
    return (
      <li key={id} className="list-group-item">
        <TodoListItem {...itemProps}
        onDelete = {() => onDelete(id)}/>
      </li>
      );
  });
  
  return (
    <ul className="list-group todo-list">
      {elements}
    </ul>
  );
}

Ну и в TodoListItem ничего не изменилось, при клике по кнопке вызывается onDelete;

      <button type="button"
              className="btn btn-outline-danger btn-sm float-right"
              onClick = {onDelete}>
        <i className="fa fa-trash-o" />
      </button>

Теперь клик по кнопке вызывает сообщение, в котором указан id того элемента, на котором произошёл клик.

Чтобы удалить этот элемент, компонент App должен удалить его из массива todoDate, на основании которого формируется список дел. Но пока этот массив не является частью state, изменить его мы не можем.