Reputation: 15319
I'm trying to get my head over React Spring. So I have some boxes, which I need to filter by an active index, and the idea is to animate the boxes rendering, but I'm only having an animation when the component renders the first time.
This is what I have so far:
import React from "react";
import ReactDOM from "react-dom";
import styled from "@emotion/styled";
import { useTransition, animated } from "react-spring";
import "./styles.css";
const Header = styled.div`
display: flex;
justify-content: center;
margin-bottom: 16px;
`;
const Content = styled.div`
display: flex;
justify-content: center;
`;
const Box = styled.div`
width: 64px;
height: 64px;
background-color: yellow;
border: 3px solid yellowgreen;
color: yellowgreen;
display: flex;
align-items: center;
justify-content: center;
font-size: 2rem;
`;
const EnhancedBox = animated(Box);
const App = () => {
const [activeBoxIndex, setActiveBoxIndex] = React.useState(0);
const boxes = [
{ label: "1", key: 0 },
{ label: "2", key: 1 },
{ label: "3", key: 2 }
];
const transition = useTransition(boxes, item => item.key, {
from: { maxHeight: "0px", overflow: "hidden", margin: "0px 0px" },
enter: { maxHeight: "100px", overflow: "hidden", margin: "5px 0px" },
leave: { maxHeight: "0px", overflow: "hidden", margin: "0px 0px" }
});
const handleBoxClick = n => () => {
setActiveBoxIndex(n);
};
return (
<div className="App">
<Header>
<button onClick={handleBoxClick(0)}>Show box 1</button>
<button onClick={handleBoxClick(1)}>Show box 2</button>
<button onClick={handleBoxClick(2)}>Show box 3</button>
</Header>
<Content>
{transition.map(({ item, props, key }) => {
return item.key === activeBoxIndex ? (
<EnhancedBox key={item.key} style={props}>
{item.label}
</EnhancedBox>
) : (
<></>
);
})}
</Content>
</div>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
I set up a Code Sandbox project to make things easier. Any help would be much appreciated. https://codesandbox.io/s/wizardly-hill-6hkk9
Upvotes: 2
Views: 1406
Reputation: 1507
There may be better ways of doing this but here's a simple approach that works:
const boxes = [
{ label: "1", key: 0 },
{ label: "2", key: 1 },
{ label: "3", key: 2 }
];
//start with item 0 in the display boxes array
const displayBoxes = [];
displayBoxes.push(boxes[0]);
const transition = useTransition(displayBoxes, item => item.key, {
from: { maxHeight: "0px", overflow: "hidden", margin: "0px 0px" },
enter: { maxHeight: "100px", overflow: "hidden", margin: "5px 0px" },
leave: { maxHeight: "0px", overflow: "hidden", margin: "0px 0px" }
});
const handleBoxClick = n => () => {
//empty the array
displayBoxes = [];
//then push in the new item
displayBoxes.push(boxes[n]);
};
One feature of useTransition that is not immediately obvious to a newcomer is that the useTransition hook acts like an observer of the current state of the array you pass to it.
To achieve the effect you're looking for, I animated height from 0 to auto but that requires a way of getting the height in px and adds a further layer of complication. I like the idea of setting the height on the element and then using maxHeight to control its appearance.
Hope that helps.
Upvotes: 2