Удаление элементов в ReactJS с использованием useState и useContext

Как новичок в программировании, я застрял с простым приложением, в котором перечислены элементы. Добавление элементов — это нормально. А вот что касается удаления одного элемента, то никак не могу понять, как реализовать эту функцию. Теперь при нажатии на элемент весь массив элементов удаляется. Я пытался поместить функцию deleteItem в другие файлы, но, кажется, перепутал ссылки.

ItemContext.js:

import React, { useState, createContext } from 'react';
import { v1 as uuidv1 } from 'uuid';
export const ItemContext = createContext();

export const ItemProvider = (props) => {
  const [items, setItems] = useState([
    {
      name: "Red Beans",
      amount: 23,
      id: uuidv1()
    },
    {
      name: "Nuts for Bunnies",
      amount: 33,
      id: uuidv1()
    },
    {
      name: "Chopped Tomatoes",
      amount: 2,
      id: uuidv1() 
    }
]);
  return ( 
      <ItemContext.Provider value={[items, setItems]} >
        {props.children}
      </ItemContext.Provider>
   );
}

AddItem.js:

    import React, { useState, useContext } from 'react';
import { ItemContext } from './ItemContext';

const AddItem = () => {
  const [name, setName] = useState('');
  const [amount, setAmount] = useState('');
  const [items, setItems] = useContext(ItemContext);

  const updateName = (e) => {
    setName(e.target.value)
  }

  const updateAmount = (e) => {
    setAmount(e.target.value)
  }

  const addItem = (e) => {
    e.preventDefault();
    setItems(prevItems => [...prevItems, {name: name, amount: amount, key: items.id}])
  }

  return ( 
      <form className="new-item" onSubmit={addItem}>
        <input type="text" name="name" value={name} onChange={updateName} placeholder="Add a new item"/>
        <input type="text" name="amount" value={amount} onChange={updateAmount} placeholder="Amount"/>
        <button>Submit</button>
      </form>
   );
}
 
export default AddItem;

Item.js:

import React, { useState, useContext } from 'react';
import { ItemContext } from './ItemContext';

const Item = ({name, amount, /*deleteItem*/ }) => {
  const [id, setId] = useState('');
  const [items, setItems] = useContext(ItemContext);

  const deleteItem = (id) => {
    // const newItems = items.filter(item => items.id !== id);
    // setItems(newItems);
    const newItems = Object.assign([], ...items)
    items.splice(id, 1);
    setItems(newItems);
  }

  return (
    <div>
      <h3>{name}</h3>
      <p>{amount}</p>
      <button onClick={() => deleteItem(items.id)} className="delete-btn">Delete</button>
    </div>
  );
}
 
export default Item;

ItemsList.js:

import React, { useState, useContext } from 'react';
import Item from './Item';
import { ItemContext } from './ItemContext';
import { v1 as uuidv1 } from 'uuid';

const ItemsList = () => {
  const [items, setItems] = useContext(ItemContext);
  const [id, setId] = useState('');

  const deleteItem = (id) => {
    // const newItems = items.filter(item => items.id !== id);
    // setItems(newItems);
    const newItems = Object.assign([], ...items)
    items.splice(id, 1);
    setItems(newItems);
  }

  return (
    <div className="items-list">
      {items.map(item => (
        <Item name={item.name} amount={item.amount} key={uuidv1()} deleteItem={deleteItem}/>
      ))}
    </div> 
   );
}
 
export default ItemsList;

person Viktor Helfrih    schedule 11.03.2021    source источник
comment
items.filter(item => items.id !== id) должно быть достаточно для удаления элемента из массива. Кажется, у вас много дублированных состояний и несколько обработчиков удаления, это может быть частью вашей проблемы. Какое состояние является настоящим items состоянием? О, подождите, я вижу, вы вытаскиваете состояние items из контекста.   -  person Drew Reese    schedule 11.03.2021


Ответы (1)


Первоначально вам нужно указать допустимое начальное значение для контекста ItemContext, который вы создаете. Его подпись должна соответствовать значению контекста. Я предлагаю использовать объект вместо массива.

export const ItemContext = createContext({
  items: [],
  addItem: () => {},
  deleteItem: () => {}
});

Затем определите обработчики addItem и deleteItem для включения в значение контекста. Вы хотите избежать прямого раскрытия функции состояния setItems, чтобы ItemProvider сохранял контроль над инвариантом состояния.

const initialState = [
  {
    name: "Red Beans",
    amount: 23,
    id: uuidv1()
  },
  {
    name: "Nuts for Bunnies",
    amount: 33,
    id: uuidv1()
  },
  {
    name: "Chopped Tomatoes",
    amount: 2,
    id: uuidv1()
  }
];

const ItemProvider = (props) => {
  const [items, setItems] = useState(initialState);

  const addItem = (item) => setItems((items) => [...items, item]);
  const deleteItem = (id) =>
    setItems((items) => items.filter((item) => item.id !== id));

  const value = {
    items,
    addItem,
    deleteItem
  };

  return (
    <ItemContext.Provider value={value}>
      {props.children}
    </ItemContext.Provider>
  );
};

Затем деструктурируйте значение ItemContext там, где вы его потребляете.

AddItem.js — обязательно назначьте новый GUID uuidV1 для новых элементов. Я думаю, что это была ваша самая большая проблема, вы назначали undefined свойству key (key: items.id) вместо того, чтобы создавать новый GUID для свойства id.

import React, { useState, useContext } from 'react';
import { ItemContext } from './ItemContext';

const AddItem = () => {
  const [name, setName] = useState("");
  const [amount, setAmount] = useState("");
  const { addItem } = useContext(ItemContext);

  const updateName = (e) => {
    setName(e.target.value);
  };

  const updateAmount = (e) => {
    setAmount(e.target.value);
  };

  const submitHandler = (e) => {
    e.preventDefault();
    addItem({
      name: name,
      amount: amount,
      id: uuidv1() // <-- new GUID here!!
    });
  };

  return (
    <form className="new-item" onSubmit={submitHandler}>
      <input
        type="text"
        name="name"
        value={name}
        onChange={updateName}
        placeholder="Add a new item"
      />
      <input
        type="text"
        name="amount"
        value={amount}
        onChange={updateAmount}
        placeholder="Amount"
      />
      <button>Submit</button>
    </form>
  );
};

Item.js — получить deleteItem из контекста и убедиться, что элемент id передан компоненту.

import React, { useState, useContext } from 'react';
import { ItemContext } from './ItemContext';

const Item = ({ name, amount, id }) => {
  const { deleteItem } = useContext(ItemContext);

  return (
    <div>
      <h3>{name}</h3>
      <p>{amount}</p>
      <button onClick={() => deleteItem(id)} className="delete-btn">
        Delete
      </button>
    </div>
  );
};

ItemsList.js — установите ключ React на текущий элемент id, а также передайте id в качестве реквизита для `Item.

import React, { useState, useContext } from 'react';
import Item from './Item';
import { ItemContext } from './ItemContext';

const ItemsList = () => {
  const { items } = useContext(ItemContext);

  return (
    <div className="items-list">
      {items.map((item) => (
        <Item
          key={item.id} // <-- Static React key to item
          id={item.id} // <-- Pass id as prop for delete handler to use
          name={item.name}
          amount={item.amount}
        />
      ))}
    </div>
  );
};

Демо

Изменить удаление-элементов-в-реагировании-использовании-использования-состояния-и-использования-контекста

person Drew Reese    schedule 11.03.2021
comment
Спасибо за ваши усилия, очень ценю это. Теперь мне стало намного понятнее, как это структурировать. - person Viktor Helfrih; 11.03.2021
comment
@ViktorHelfrih Нет проблем. Обычно мне нравится проверять мои более крупные ответы, чтобы убедиться, что все части работают вместе, как я подозреваю, вы этого и хотели. Если это в достаточной степени решает вашу проблему с удалением элементов, я приглашаю вас принять этот ответ, и если вы нашли его и объяснения полезными, пожалуйста, также проголосуйте за него, когда сможете. Здоровья и удачи. - person Drew Reese; 11.03.2021
comment
Я попытался подключить этот проект к базе данных firebase. Но почему-то я все еще сталкиваюсь с проблемами, например. состояние не обновляется сразу, когда я добавляю новый элемент, и функция удаления не работает. Я безуспешно пытался скопировать компонент элемента в компонент списка элементов. Я обнаружил, что сделал бесконечный цикл в useEffect. Сейчас исправил, но не уверен, что все в порядке. Я просто не могу поверить, что не могу найти решение для правильно работающего простого приложения. Или, возможно, мне следует добавить куда-нибудь больше useEffects. Если у вас есть время, вы можете посмотреть мой код, если я его загружу? - person Viktor Helfrih; 25.04.2021