I am trying to animate the transition of a list when filtered using the new useTransition hook changes in react-spring v9.x so that as list items are filtered the remaining items move to their new positions.
So far I have managed to get components in the list to fade in and out but the remaining components just jump to their new positions instantly once the fade out animation has completed. I have not been able to change this.
How do I animate the remaining components to smoothly move to their new locations?
Here is a code sandbox link to the current code.
You can see the jumping effect clearest if you type 'p' into search bar and watch the component with the name Plum jump up after a short delay.
App.js
import { useState } from "react";
import { useSpring, useTransition, animated } from "react-spring";
export default function App() {
const [items, setItems] = useState([
{ name: "Apple", key: 1 },
{ name: "Banana", key: 2 },
{ name: "Orange", key: 3 },
{ name: "Kiwifruit", key: 4 },
{ name: "Plum", key: 5 }
]);
const [searchText, setSearchText] = useState("");
const filteredItems = items.filter((item) =>
item.name.toLowerCase().includes(searchText.toLowerCase())
);
const transition = useTransition(filteredItems, {
from: { opacity: 0 },
enter: { opacity: 1 },
leave: { opacity: 0 }
});
const fadeInListItems = transition((style, item) => {
return (
<animated.div style={style}>
<Item data={item} />
</animated.div>
);
});
const handleSearchBarChange = ({ target }) => setSearchText(target.value);
return (
<div className="App">
<h2>Click on an item to toggle the border colour.</h2>
<SearchBar onChange={handleSearchBarChange} value={searchText} />
{fadeInListItems}
</div>
);
}
const SearchBar = (props) => {
return (
<>
<label>Search Bar: </label>
<input onChange={props.onChange} value={props.searchText} type="text" />
</>
);
};
const Item = (props) => {
const [isClicked, setIsClicked] = useState(false);
const [styles, api] = useSpring(() => ({
border: "2px solid black",
margin: "5px",
borderRadius: "25px",
boxShadow: "2px 2px black",
backgroundColor: "white",
color: "black"
}));
const handleClick = (e) => {
api.start({
backgroundColor: isClicked ? "white" : "red",
color: isClicked ? "black" : "white"
});
setIsClicked((prev) => !prev);
};
return (
<animated.div style={styles} onClick={handleClick} key={props.data.key}>
{props.data.name}
</animated.div>
);
};