Etika49
Etika49

Reputation: 742

React.js remove item from Array in state syntax

I would like to remove an item from array onClick by using the prevState syntax.

Here is an example code that i use:

export default function App() {
  const [selected, setSelected] = useState(false);
  const [selectedItems, setSelectedItems] = useState([]);
  const [cards, setCards] = useState(["card1", "card2", "card3", "card4"]);

  function handleButtonClick(event) {
    setSelected(true);
    let key = event.target.id;
    key = parseInt(key, 10);
    if (selectedItems.indexOf(key) === -1) {
      
      setSelectedItems([...selectedItems, key]);
    } else {
      //  HOW do i remove the item from the array ????
      let index = selectedItems.indexOf(key);
    }
  }

  return (
    <div className="App">
      {cards.map((card, index) => (
        <button
          className={
            selected && selectedItems.indexOf(index) !== -1
              ? "button1 active"
              : "button1"
          }
          onClick={handleButtonClick}
          id={index}
        >
          {card}
        </button>
      ))}
    </div>
  );
}

link to sandbox as well : https://codesandbox.io/s/patient-thunder-mb1vx?file=/src/App.js

Upvotes: 1

Views: 156

Answers (6)

T.J. Crowder
T.J. Crowder

Reputation: 1074385

I find it odd that the simple, standard answer hasn't been posted:

function handleButtonClick(event) {
    setSelected(true);
    const key = parseInt(event.target.id, 10);
    setSelectedItems(selectedItems => {
        const itemIndex = selectedItems.indexOf(key);
        if (itemIndex === -1) {
            // New item, add it
            selectedItems = [...selectedItems, key];
        } else {
            // New item, add it
            selectedItems = selectedItems.filter((_, index) => index !== itemIndex); // ***
        }
        return selectedItems;
    });
}

Notice that since this sets state based on existing state, it's best to use the callback form of the state setter.

Upvotes: 0

AKX
AKX

Reputation: 169032

An option using Array#splice that uses the function form of setState:

const [items, setItems] = React.useState([]);
const deleteItemAtIndex = (index) => setItems(items => {
  const newItems = [...items];
  newItems.splice(index, 1);
  return newItems;
});

This has the advantage that it can be memoized (useCallback) without needing items as a dependency (since setItems is always stable and the function itself doesn't use items, only the callback does):

const deleteItemAtIndex = React.useCallback((index) => setItems(items => {
  const newItems = [...items];
  newItems.splice(index, 1);
  return newItems;
}), []);

Upvotes: 1

Tur1ng
Tur1ng

Reputation: 834

An alternative to @CertainPerformance using splice:

const copy = [...selectedItems];
const index = copy.indexOf(key);
copy.splice(index, 1);
setSelectedItems(copy);

Upvotes: 1

Nam Bui
Nam Bui

Reputation: 1361

You can make a copy of your array, splice the index from the copy then set the state with the new array.

const newArray = [...oldArray]
newArray.splice(index, 1)
setSelectedItems(newArray)

Upvotes: 1

Zahid Hasan
Zahid Hasan

Reputation: 622

Use this one. Hope it will work.

if (selectedItems.indexOf(key) === -1) {
  setSelectedItems([...selectedItems, key]);
} else {
  //  HOW do i remove the item from the array ????
  let index = selectedItems.indexOf(key);
  const arr = selectedItems.filter((item, i) => i !== index);
  setSelectedItems(arr);
}

Check this link https://codesandbox.io/s/stupefied-greider-m25le

Upvotes: 1

CertainPerformance
CertainPerformance

Reputation: 370779

Once you've found the index of the key in selectedItems, set selectedItems's state by slicing the array before and after that index:

const index = selectedItems.indexOf(key);
setSelectedItems([
  ...selectedItems.slice(0, index),
  ...selectedItems.slice(index + 1)
]);

const { useState } = React;

const App = () => {
  const [selected, setSelected] = useState(false);
  const [selectedItems, setSelectedItems] = useState([]);
  const [cards, setCards] = useState(["card1", "card2", "card3", "card4"]);

  function handleButtonClick(event) {
    setSelected(true);
    let key = event.target.id;
    key = parseInt(key, 10);
    if (selectedItems.indexOf(key) === -1) {
      let index = selectedItems.indexOf(key);
      setSelectedItems([...selectedItems, key]);
    } else {
      const index = selectedItems.indexOf(key);
      setSelectedItems([
        ...selectedItems.slice(0, index),
        ...selectedItems.slice(index + 1)
      ]);
    }
  }

  return (
    <div className="App">
      {cards.map((card, index) => (
        <button
          className={
            selected && selectedItems.indexOf(index) !== -1
              ? "button1 active"
              : "button1"
          }
          onClick={handleButtonClick}
          id={index}
          key={index}
        >
          {card}
        </button>
      ))}
    </div>
  );
}

ReactDOM.render(<App />, document.querySelector('.react'));
.App {
  font-family: sans-serif;
  text-align: center;
}

.active {
  border: 2px solid green;
}
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div class="react"></div>

Also remember to use a unique key prop for lists, and to prefer const over let whenever possible.

Upvotes: 1

Related Questions