Home > Software design >  React: How can I remove a specific div element after clicking its associated button?
React: How can I remove a specific div element after clicking its associated button?

Time:09-23

I have this component which adds a div and its elements to the dom on button click. The adding part works fine as expected but the issue arises when I want to delete.

Right now when I click on the delete button, it does remove the item but it doesn't remove that specific item which the button is associated with. It just removes the div from the top or bottom.

I have been trying to remove that specific div whose button has been clicked to remove. How can I achieve that?

Here's the CodeSandbox.

And here's the code:

import { useState } from "react";

const App = () => {

  const [ counter, setCounter ] = useState( 1 );

  const handleAddDiv = () => {
    setCounter( counter   1 );
  };

  const handleRemoveDiv = () => {
    setCounter( counter - 1 );
  };

  return (
    <div className="App">

      {
        Array.from(Array( counter )).map(( item, idx ) => (
           <div>
             <div>
               <input type="text" />
               <button onClick={handleRemoveDiv}>Remove</button>
             </div>
           </div>
        ))
      }

      <button onClick={handleAddDiv}>Add</button>

    </div>
  );
}
export default App;

CodePudding user response:

Map over an array of unique Ids

First of all, you should map over an array of items instead of an integer value.

So, on click of add button, you should push a unique ID to the array of items where each ID would denote an item being rendered in your app.

Now, when you click on remove button, you would need to remove that ID from the array of items, which would result in "deletion" of that div from the app.

In my case, I have considered timestamp as a unique ID but should explore other options for generating unique IDs. Working with indices is anti pattern in React especially when you are mapping over an array in JSX as you would encounter issues at one point of time. So, it's a good idea to maintain unique Ids.

Note: Damian's solution is not ideal as DOM Manipulation is avoided in React.

const { useState, useCallback } = React;

const Item = ({ id, removeDiv }) => {
  const clickHandler = useCallback(() => {
    removeDiv(id);
  }, [id, removeDiv]);

  return (
    <div>
      <input type="text" />
      <button onClick={clickHandler}>Remove</button>
    </div>
  );
};

const App = () => {
  const [items, setItems] = useState([]);

  const addDiv = useCallback(() => {
    // using timestamp as a unique ID
    setItems([...items, new Date().getTime()]);
  }, [items]);

  const removeDiv = useCallback((itemId) => {
    // filter out the div which matches the ID
    setItems(items.filter((id) => id !== itemId));
  }, [items]);

  return (
    <div className="app">
      {items.map((id) => (
        <Item key={id} id={id} removeDiv={removeDiv} />
      ))}

      <button onClick={addDiv}>Add</button>
    </div>
  );
};

ReactDOM.render(<App />,document.getElementById("react"));
.app {
  text-align: center;
  font-family: sans-serif;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="react"></div>

CodePudding user response:

I would use an array state instead of a counter state, because otherwise you don't know which element has to be removed.

import { useState } from "react";
import "./styles.css";

let counter = 1;
export default function App() {
  const [array, setArray] = useState([0]);

  const handleAddDiv = () => {
    setArray((prev) => [...prev, counter  ]);
  };

  const handleRemoveDiv = (idx) => {
    var arrayCopy = [...array];
    arrayCopy.splice(idx, 1);//remove the the item at the specific index
    setArray(arrayCopy);
  };

  return (
    <div className="App">
      {array.map((item, idx) => (
        <div key={item}>
          <div>
            <input type="text" />
            <button onClick={()=>handleRemoveDiv(idx)}
            >Remove</button>
          </div>
        </div>
      ))}
      <button onClick={handleAddDiv}>Add</button>
    </div>
  );
}

When I am adding a new item, I give it the value counter , because I will use it as a key, and a key should be unique.

CodePudding user response:

This is not prefered react way of doing things, but this will work:

import "./styles.css";

import { useState } from "react";

const App = () => {
  const [counter, setCounter] = useState(1);

  const handleAddDiv = () => {
    setCounter(counter   1);
  };

  const removeNode = (idx) => document.getElementById(`id-${idx}`).remove();

  return (
    <div className="App">
      {Array.from(Array(counter)).map((item, idx) => (
        <div key={idx} id={`id-${idx}`}>
          <div>
            <input type="text" />
            <button onClick={() => removeNode(idx)}>Remove</button>
          </div>
        </div>
      ))}

      <button onClick={handleAddDiv}>Add</button>
    </div>
  );
};

export default App;

Generaly if you would like to have it made correactly then you would want to map on a real array and have every item in array eighter having an unique id or simply use map index and then based on which item you click write a function to remove from that array our specific element.

  • Related