0
votes

I have a react app in which I'm trying to make each table row fade out whenever a corresponding button to that table row is clicked. The idea is that when a delete button on that row is clicked the whole row will fade out and the rest of the rows below it will move up (without creating any empty space) and if it is the last row that is being removed it will just fade out and table will become smaller.

Currently I'm trying to use refs and CSS to implement this feature, however right now if, for example, I click on "Delete" button in the second row - this row will be removed immediately without any animation, but at the same time the very last row's content will fade out according to the CSS styling that I set up and there will be an empty table row left there. Following that, any other element will be removed without any fade out animation.

So far I have done the following:

1) A component that create dynamic table rows (any necessary data, including the ref, are passed through useContext() from the component with the business logic):

function TableData() {
  const { dataset, handleDeletion, wrapperRef } = React.useContext(Context)

  return dataset.map((item, index) => {
    const { id, name, year, color, pantone_value } = item
    return (
      <tr key={id} ref={wrapperRef}>
        <td>{year}</td>
        <td>{name}</td>
        <td style={{ backgroundColor: color, width: "100px" }} />
        <td>
          <button 
            value={id}
            type="button" 
            class="btn btn-outline-danger" 
            onClick={handleDeletion}>
            <i class="far fa-trash-alt"></i>
          </button>
        </td>
      </tr>
    )
  })
}

2) In the main component with business logic I set up the ref:

const wrapperRef = React.createRef()

3) And this is a function that handle a click of a delete button:

function handleDeletion(event) {
  const id = event.currentTarget.value

  const wrapper = wrapperRef.current
  wrapper.classList.add('fade')

  setDataset(prevDataset => prevDataset.filter(item => item.id !== parseInt(id)))
}

4) Simple CSS animation:

.fade {
  opacity: 0;
  transition: opacity 5000ms;
}

Could you please let me know what am I doing wrong here and what would need to be fix in order to make it work as planned?

Best regards, Konstantin

P.S. I'm attaching the full code to this post as well (just in case) and here is the link to the pen with this project.

UPDATE:

1) I added the following className into my 'tr' component:

<tr key={id} className={toDelete && itemToDelete === id ? 'fade' : ''}>

2) In the main component I added the following state hooks:

const [ toDelete, setToDelete ] = React.useState(false)
const [ itemToDelete, setItemToDelete ] = React.useState('')

3) And changed the delete function to the following (with adding an extra useEffect that is applied with those hooks being changed):

function handleDeletion(event) {
  const id = event.currentTarget.value

  setToDelete(true)
  setItemToDelete(id)

  //setDataset(prevDataset => prevDataset.filter(item => item.id !== parseInt(id)))
}

React.useEffect(() => {
  if (toDelete !== false && itemToDelete !== '') {
    setTimeout(() => {
      setDataset(prevDataset => prevDataset.filter(item => item.id !== parseInt(itemToDelete)))
    }, 500)
  }
}, [toDelete, itemToDelete])

I tried to do it different ways, including using setTimeout/setInterval, doing it without a separate useEffect hook in the handleDeletion function, etc. But still can't get the desired result, it is removing it without applying fading animation.

2

2 Answers

1
votes

You create wrapperRef and you overwrite it with every map iteration so that it holds the reference on the last table row. That is why you see the last row fade out.

You would probably need an array of refs so that you have the ref for every row. Depending on how big the table is maybe you should look for other solutions because it not recommended to overuse refs

This approach is probably the best.

1
votes

In addition to Darko's answer, you can also delete the row from the DOM after fading out.

setTimeout(function() { that.setState({ isDeleted: true })}, 500);

https://codepen.io/primaryobjects/pen/qBaMvrq