I'm trying to write a React component which contains a panel of buttons, and each of the buttons needs to be able to be independently switched in and out depending on the state of the app. To do this I've created a useTransition
, wrapped in a custom hook, which is supposed to trigger the transition when it recieves a new SVG functional component.
The issue I'm having is that while the buttons are currently transitioning in correctly, I can't seem to get the useTransition
to swap them out when it receives a new component. It will remove one when the component is replaced with an empty array, which is intended, and a useEffect
inside the hook triggers when a new component is passed in, but for some reason that change is not picked up by the useTransition
.
I'm guessing it has something to do with object equality between the components but I don't know how to work around it without forcing the change with window.requestAnimationFrame
to set the state twice. Here is a codesandbox with a minimal demo.
Parent Component:
import React, { useState } from "react";
import { a } from "react-spring";
import "./styles.css";
import PlaySVG from "./svgs/PlaySVG";
import SaveSVG from "./svgs/SaveSVG";
import SearchSVG from "./svgs/SearchSVG";
import TrashSVG from "./svgs/TrashSVG";
import { useVertTransition } from "./Hooks";
export default function ActionButtons() {
const svgs = {
play: <PlaySVG style={{ width: "100%", height: "auto" }} id="play" />,
search: <SearchSVG style={{ width: "80%", height: "auto" }} id="search" />,
save: <SaveSVG style={{ width: "80%", height: "auto" }} id="save" />,
trash: <TrashSVG style={{ width: "80%", height: "auto" }} id="trash" />
};
const [actions, setActions] = useState({
one: svgs.play,
two: svgs.search,
three: svgs.trash
});
const [slotOne] = useVertTransition(actions.one);
const [slotTwo] = useVertTransition(actions.two);
const [slotThree] = useVertTransition(actions.three);
function buttonHandler() {
setActions({
...actions,
one: [],
two: svgs.save
});
}
return (
<div className="container">
<div className="panel">
<div className="button-container">
{slotOne.map(({ item, props, key }) => (
<a.button key={key} style={props} onClick={buttonHandler}>
{item}
</a.button>
))}
</div>
<div className="button-container">
{slotTwo.map(({ item, props, key }) => (
<a.button key={key} style={props} onClick={buttonHandler}>
{item}
</a.button>
))}
</div>
<div className="button-container">
{slotThree.map(({ item, props, key }) => (
<a.button key={key} style={props} onClick={buttonHandler}>
{item}
</a.button>
))}
</div>
</div>
</div>
);
}
useVertTransition hook:
import React, { useEffect } from "react";
import { useTransition } from "react-spring";
export function useVertTransition(item) {
useEffect(() => {
console.log(item);
}, [item]);
const vertTransition = useTransition(item, null, {
from: { transform: "translateY(-20px) rotateX(-90deg)", opacity: 0 },
enter: { transform: "translateY(0px) rotateX(0deg)", opacity: 1 },
leave: { transform: "translateY(20px) rotateX(90deg)", opacity: 0 },
trail: 400,
order: ["leave", "enter"]
});
return [vertTransition];
}
SVG component example:
import React from "react";
function PlaySVG({ style }) {
return (
<svg xmlns="http://www.w3.org/2000/svg" style={style} viewBox="0 0 24 24">
<path d="M7 6L7 18 17 12z" />
</svg>
);
}
export default PlaySVG;