React - fade in on add, fade out on remove item

Viewed 7140

Hello fellow react developers!

I am trying to make a list of items, with two basic operations: add item and remove item.

What I want to do is whenever I add an item, I want it to have a nice fade in effect (newly added item fades in), and when I remove it, I want it to fade-out (the removed item fades out).

What would be the simplest or most straightforward way of achieving this effect on the given implementation (or if the implementation needs to be adjusted to do it, that works as well :))?

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

  return (
    <div>
      <button 
       className='button-add' 
        onClick={() => setItems([...items, {
            id: new Date().getUTCMilliseconds().toString()}])}
      >
        Add item
      </button>
      {items.map(item => (
        <div className='item'>
          <span className='item-name'>{item.id}</span>
        <button className='button-remove' onClick={() => setItems(items.filter((itemInner) => itemInner.id !== item.id))}>Remove item</button>
        </div>
      ))}
    </div>
  )
}

Working example (add/remove) on codepen

1 Answers

Keyframes are what you are looking for.

I came up with this solution, you might need to tweak it to fit your needs:

Css file:

.item {
    -webkit-animation: fadein .3s linear forwards;
    animation: fadein .3s linear forwards;
    padding: 10px;
}
.item-fadeout{
    display: flex;
    align-items: center;
    padding: 10px;
    -webkit-animation: fadeout .3s linear forwards;
    animation: fadeout .3s linear forwards;
}
    
@-webkit-keyframes fadein {
    0% { opacity: 0; }
    100% { opacity: 1; }
}
    
@keyframes fadein {
    0% { opacity: 0; }
    100% { opacity: 1; }
}
    
@-webkit-keyframes fadeout {
    0% { opacity: 1; }
    100% { opacity: 0; }
}
    
@keyframes fadeout {
    0% { opacity: 1; }
    100% { opacity: 0; }
}

JS:

const { useState } = React;
    
function Item(props) {
    const [isFadingOut, setIsFadingOut] = useState(false);
    
    const fadeOut = (cb) => {
        setIsFadingOut(true);
        cb();
    };
    const handleRemoveItem = () => {
        props.removeItem();
        setIsFadingOut(false);
    };
    return (
        <div className={isFadingOut ? 'item-fadeout' : 'item'}>
            <span className='item-name'>{props.item.id}</span>
            <button
              className='button-remove'
              onClick={() => fadeOut(setTimeout(() => handleRemoveItem(), 300))}
            >
                Remove item
            </button>
        </div>
    );
}
    
function App() {
    const [items, setItems] = useState([]);
    
    return (
        <div>
            <button
              className='button-add'
              onClick={() =>
                setItems([
                    ...items,
                    {
                        id: new Date().getUTCMilliseconds().toString(),
                    },
                ])
              }
            >
                Add item
            </button>
            {items.map((item) => (
                <Item
                  item={item}
                  removeItem={() =>
                      setItems(items.filter((itemInner) => itemInner.id !== item.id))
                  }
                />
            ))}
         </div>
    );
}
    
ReactDOM.render(<App />, document.getElementById('app'));

Working sample: https://codepen.io/luismendes535/pen/YzyJXdR

Related