Удалить элемент из списка в React

  • Михаил
  • 8 мин. на прочтение
  • 185
  • 27 Jun 2020
  • 27 Jun 2020

В React обычная задача — удалить элемент из списка. Здесь я хочу кратко показать вам, как это работает. Каждый раз, когда вы хотите что-то изменить в React, например список, из которого вы хотите удалить элемент, вам нужно использовать управление состоянием React . Здесь мы будем использовать хук useState из React, чтобы упростить первый пример, однако вы также можете использовать хук useReducer из React, как вы увидите позже.

Мы начнем с типичного списка в React , где мы предоставляем атрибут стабильного ключа для каждого отображаемого элемента списка:

import React from 'react';
const list = [
 {
   id: 'a',
   firstname: 'Robin',
   lastname: 'Wieruch',
   year: 1988,
 },
 {
   id: 'b',
   firstname: 'Dave',
   lastname: 'Davidds',
   year: 1990,
 },
];
const App = () => {
 return (
   <ul>
     {list.map((item) => (
       <li key={item.id}>
         <span>{item.firstname}</span>
         <span>{item.lastname}</span>
         <span>{item.year}</span>
       </li>
     ))}
   </ul>
 );
};
export default App;

Пока что список представляет собой просто переменную JavaScript и еще не сохраняет состояние. Чтобы изменить его, в данном случае удалить из него элемент, нам нужно сделать список сохраняющим состояние с помощью состояния React и его хука useState :

const initialList = [
 {
   id: 'a',
   firstname: 'Robin',
   lastname: 'Wieruch',
   year: 1988,
 },
 {
   id: 'b',
   firstname: 'Dave',
   lastname: 'Davidds',
   year: 1990,
 },
];
const App = () => {
 const [list, setList] = React.useState(initialList);
 return (
   <ul>
     {list.map((item) => (
       <li key={item.id}>
         <span>{item.firstname}</span>
         <span>{item.lastname}</span>
         <span>{item.year}</span>
       </li>
     ))}
   </ul>
 );
};

Теперь у нас есть список с состоянием, и мы можем его изменить. Давайте добавим кнопку с функцией-обработчиком , которая обрабатывает событие щелчка для каждого элемента в списке. В этом случае должна быть кнопка удаления элемента:

const App = () => {
 const [list, setList] = React.useState(initialList);
 function handleRemove() {
   // remove item
 }
 return (
   <ul>
     {list.map((item) => (
       <li key={item.id}>
         <span>{item.firstname}</span>
         <span>{item.lastname}</span>
         <span>{item.year}</span>
         <button type="button" onClick={handleRemove}>
           Remove
         </button>
       </li>
     ))}
   </ul>
 );
};

Поскольку мы находимся в сопоставленном списке, нам нужно выяснить, как передать конкретный элемент или идентификатор элемента, который мы хотим удалить из списка, в функцию-обработчик. Самый простой подход к этому — использовать встроенный обработчик для ввода элемента или, в данном случае, идентификатора элемента в качестве параметра:

const App = () => {
 const [list, setList] = React.useState(initialList);
 function handleRemove(id) {
   console.log(id);
   // remove item
 }
 return (
   <ul>
     {list.map((item) => (
       <li key={item.id}>
         <span>{item.firstname}</span>
         <span>{item.lastname}</span>
         <span>{item.year}</span>
         <button type="button" onClick={() => handleRemove(item.id)}>
           Remove
         </button>
       </li>
     ))}
   </ul>
 );
};

Единственное, чего не хватает, — это удаление определенного элемента из списка всякий раз, когда происходит нажатие на кнопку. Мы сделаем это, изменив текущий список состояний с помощью функции фильтра:

const App = () => {
 const [list, setList] = React.useState(initialList);
 function handleRemove(id) {
   const newList = list.filter((item) => item.id !== id);
   setList(newList);
 }
 return (
   <ul>
     {list.map((item) => (
       <li key={item.id}>
         <span>{item.firstname}</span>
         <span>{item.lastname}</span>
         <span>{item.year}</span>
         <button type="button" onClick={() => handleRemove(item.id)}>
           Remove
         </button>
       </li>
     ))}
   </ul>
 );
};

Вместо того, чтобы изменять список, мы сохраняем его как неизменяемую структуру данных и поэтому создаем новый список на основе старого списка и условия фильтра. Это потому, что функция фильтра не изменяет список, а только возвращает новый список.

Теперь, когда вызывается наша функция обновления состояния из React useState Hook, список без элемента устанавливается как новое состояние, и компонент выполняет повторную визуализацию для отображения только оставшихся элементов. Это все, что нужно знать об удалении записи из массива в React. Но есть еще...

Например, в нашем случае все происходит в одном компоненте. Что произойдет, если вы захотите удалить элемент из списка дочернего компонента? Давайте продолжим разделение компонента на несколько компонентов. Нам понадобится обработчик обратного вызова для передачи функциональности в виде деструктурированных реквизитов для удаления элемента:

const App = () => {
 const [list, setList] = React.useState(initialList);
 function handleRemove(id) {
   const newList = list.filter((item) => item.id !== id);
   setList(newList);
 }
 return <List list={list} onRemove={handleRemove} />;
};
const List = ({ list, onRemove }) => (
 <ul>
   {list.map((item) => (
     <Item key={item.id} item={item} onRemove={onRemove} />
   ))}
 </ul>
);
const Item = ({ item, onRemove }) => (
 <li>
   <span>{item.firstname}</span>
   <span>{item.lastname}</span>
   <span>{item.year}</span>
   <button type="button" onClick={() => onRemove(item.id)}>
     Remove
   </button>
 </li>
);

Вот и все. Вы можете удалить элемент из дочернего компонента, тогда как список управляется как состояние где-то вверху в родительском компоненте. Если вы хотите управлять списком как состоянием в компоненте List, а не в компоненте App, вам придется поднять состояние .

Теперь мы продолжим замену useState React на хук useReducer Hook React . Хук-редьюсер можно использовать в React для сложных состояний и сложных переходов состояний. В настоящее время это не относится к нашему штату, но это может представлять интерес для вашего конкретного случая в будущем. Начнем с определения функции редуктора для управления списком с состоянием:

const listReducer = (state, action) => {
 switch (action.type) {
   case 'REMOVE_ITEM':
     return state.filter((item) => item.id !== action.id);
   default:
     throw new Error();
 }
};

По сути, функция редуктора принимает состояние и действие в качестве входных данных и возвращает новое состояние на основе этой информации в качестве выходных данных. Кроме того, у него есть ветка для каждого типа действий. В этом случае существует только один тип действия и, следовательно, одна ветвь для удаления элемента. Фактическая логика удаления элемента из списка теперь перенесена из нашей функции-обработчика в этот редуктор.

Далее мы заменим хук useState компонента на хук useReducer. Этот хук возвращает состояние и функцию отправки в виде массива, к которому мы снова удобно получаем доступ через деструктуризацию массива . Функция диспетчеризации затем используется в нашей функции-обработчике, передавая ей соответствующее действие:

const App = () => {
 const [list, dispatchList] = React.useReducer(
   listReducer,
   initialList
 );
 function handleRemove(id) {
   dispatchList({ type: 'REMOVE_ITEM', id });
 }
 return <List list={list} onRemove={handleRemove} />;
};

Вот и все, что касается использования useReducer вместо useState. Оба хука состояния полезны в React, поэтому вам следует решить, исходя из ваших потребностей, нужен ли вам хук useReducer или useState .

И последнее, но не менее важное: не всегда ваш штат может быть только списком. Часто у вас будет более сложный объект состояния, и список представляет собой только одно свойство этого объекта. Как бы вы тогда удалили элемент из этого списка в объекте? Давайте сначала рассмотрим этот пример с помощью хука useState в React. Допустим, рядом со списком есть логический флаг, позволяющий показать или скрыть список с помощью условного рендеринга :

const App = () => {
 const [listData, setListData] = React.useState({
   list: initialList,
   isShowList: true,
 });
 function handleRemove(id) {
   // this doesn't work yet
   const newList = list.filter((item) => item.id !== id);
   // this doesn't work yet
   setList(newList);
 }
 if (!listData.isShowList) {
   return null;
 }
 return <List list={listData.list} onRemove={handleRemove} />;
};

Мы начнем со сложного объекта состояния, одним из свойств которого является список. Где бы мы ни хотели использовать список (или логический флаг), нам нужно сначала получить доступ к свойству объекта. Единственное, чего не хватает, так это исправления функции-обработчика, потому что она больше не может работать только со списком, а должна учитывать объект:

const App = () => {
 const [listData, setListData] = React.useState({
   list: initialList,
   isShowList: true,
 });
 function handleRemove(id) {
   const newList = listData.list.filter((item) => item.id !== id);
   setListData({ ...listData, list: newList });
 }
 if (!listData.isShowList) {
   return null;
 }
 return <List list={listData.list} onRemove={handleRemove} />;
};

Опять же, мы обращаемся к свойству списка объекта, чтобы фильтровать список на основе входящего идентификатора. Затем нам нужно снова обновить состояние с помощью сложного объекта состояния. Мы могли бы явно установить и новый список, и логический флаг (который не изменился), но в этом случае мы используем оператор расширения JavaScript для распространения всех пар ключ/значение из объекта состояния в новый объект состояния, в то время как переопределение свойства списка новым списком. Давайте применим ту же технику для примера с функцией редуктора:

const listReducer = (state, action) => {
 switch (action.type) {
   case 'REMOVE_ITEM':
     return {
       ...state,
       list: state.list.filter((item) => item.id !== action.id),
     };
   default:
     throw new Error();
 }
};
const App = () => {
 const [listData, dispatchListData] = React.useReducer(listReducer, {
   list: initialList,
   isShowList: true,
 });
 function handleRemove(id) {
   dispatchListData({ type: 'REMOVE_ITEM', id });
 }
 if (!listData.isShowList) {
   return null;
 }
 return <List list={listData.list} onRemove={handleRemove} />;
};

Вот и все. Как и в предыдущей версии, мы просто применяем все изменения к сложному объекту состояния, свойство которого имеет список, а не используем список непосредственно в качестве состояния. Удаление элемента из списка остается прежним.