Как мне удалить компонент из массива состояния реакции без удаления остальной части массива?

#javascript #arrays #reactjs #react-hooks #state

#javascript #массивы #reactjs #реагирующие хуки #состояние

Вопрос:

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

App.js:

 import React from 'react' 
import Parent from './Parent';
import './App.css';

function App() {
  return (
    <div className="App">
      <Parent />
    </div>
  );
}

export default App;
 

Parent.js:

 import React, { useState } from 'react';
import ListItem from './ListItem';
import './App.css';

function Parent() {
  const [itemList, setItemList] = useState([])
  const [numbers, setNumbers] = useState([])

  const addItem = () => {
    const id = Math.ceil(Math.random()*10000)
    const newItem = <ListItem 
      id={id}
      name={'Item-'   id}
      deleteItem={deleteItem}
    />
    const list = [...itemList, newItem]
    setItemList(list)
  };

  const deleteItem = (id) => {
    let newItemList = itemList;
    newItemList = newItemList.filter(item => {
        return item.id !== id
    })
    setItemList(newItemList);
  }

  const addNumber = () => {
    const newNumbers = [...numbers, numbers.length   1]
    setNumbers(newNumbers)
  }

  const deleteNum = (e) => {
    let newNumbers = numbers
    newNumbers = newNumbers.filter(n => n !==  e.target.innerHTML)
    setNumbers(newNumbers);
  }

  return (
    <div className="Parent">
      List of items:
      <div>
        {itemList}
      </div>
      <button onClick={addItem}>
        Add item
      </button>
      <div>
        List of numbers:
        <div>
          {numbers.map(num => (
            <div onClick={deleteNum}>{num}</div>
          ))}
        </div>
      </div>
      <button onClick={addNumber}>
        Add number
      </button>
    </div>
  );
};

export default Parent;
 

ListItem.js:

 import React from 'react';
import './App.css';

function ListItem(props) {

  const { id, name, deleteItem } = props;

  const handleDeleteItem = () => {
    deleteItem(id);
  }

  return (
    <div className="ListItem" onClick={handleDeleteItem}>
      <div>{name}</div>
    </div>
  );
};

export default ListItem;
 
  1. Когда я добавляю элемент, нажимая кнопку, родительское состояние обновляется правильно.
  2. Когда я нажимаю на элемент (чтобы удалить его), он удаляет сам себя, а также каждый элемент в массиве, который появляется после него <— НЕЖЕЛАТЕЛЬНОЕ ПОВЕДЕНИЕ. Я хочу удалить только определенный элемент.
  3. Я также тестировал его с числами (не создавая отдельный компонент). Они удаляются правильно — удаляется только отдельный номер, на который я нажимаю.

Насколько я могу судить, отдельные компоненты элемента сохраняют ссылку на то, каким было значение родительского состояния при их создании. Мне это кажется очень странным поведением…

Как мне удалить только отдельный элемент из массива состояний ItemList, если они состоят из отдельных компонентов?

Спасибо

РЕДАКТИРОВАТЬ: согласно инструкции от Bergi, я исправил проблему, преобразовав значение состояния ‘ItemList’ в массив объектов для визуализации (и повторного отображения) при изменении списка:

     const addItem = () => {
      const id = Math.ceil(Math.random()*10000);
      const newItem = {
        id: id,
        name: 'Item-'   id,
      }
      const newList = [...itemList, newItem]
      setItemList(newList)
    }
 

     React.useEffect(() => {

    }, [itemList]);
 

 <div className="Parent">
    List of items:
    <div>
        {itemList.map(item => {
        return (<ListItem
            id={item.id}
            name={item.name}
            deleteItem={deleteItem}
        />);
    })}
...
 

Комментарии:

1. Не используйте Math.ceil(Math.random()*10000) для идентификаторов, но itemList.length

2. Это приведет к удалению более одного элемента в будущем: 1) Создайте 5 элементов (идентификаторы 0, 1, 2, 3, 4) — 2) Удалите 3-й элемент (идентификатор 2) — 3) Добавьте еще два элемента (идентификаторы теперь равны 0, 1,3, 4, 4, 5)

Ответ №1:

Проблема в том, что ваша deleteItem функция является закрытием старого itemList , начиная с момента создания элемента. Два решения:

  • используйте форму обратного вызова setItemList
  • не храните элементы react в этом списке, а просто обычные объекты (которые вы можете использовать в качестве реквизитов) и передайте (самую последнюю) deleteItem функцию только при рендеринге ListItem

Комментарии:

1. Спасибо, Берги, это решило проблему! К вашему сведению, я использовал метод простых объектов. Я обновлю свой исходный пост для всех, кто еще сталкивается с той же проблемой.