Удалить элемент из списка в React
В 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} />;
};
Вот и все. Как и в предыдущей версии, мы просто применяем все изменения к сложному объекту состояния, свойство которого имеет список, а не используем список непосредственно в качестве состояния. Удаление элемента из списка остается прежним.
Только полноправные пользователи могут оставлять комментарии. Аутентифицируйтесь пожалуйста, используя сервисы.