I'm using react spring's useSprings hook. My implementation of this hook takes a variable number for the count, and then has an associated configuration.
My hook implementation looks like this:
const [activeTokens, setActiveTokens] = useState([]);
const [tokenSprings, setTokenSprings] = useSprings(activeTokens.length, (index) => {
return {
from: { transform: `translate3d(0px, 0px, 0)`},
to: async (next) => {
var rotate = randomInt(800, 7200);
await next({
transform: `translate3d(1px, 1px, 0)`,
config: { mass: 0.4, tension: 5, friction: 3 },
})
},
}
}
});
I'm able to add tokens and render the springs as expected. The issue I'm running into is when I want to remove specific tokens from the DOM. The jsx looks as follows:
tokenSprings.map((spring, index) => (
<animated.div className="text-white text-2xl absolute cursor-pointer" key={index} style={spring}>
<div onClick={() => handleTokenClick(index)}>
<FontAwesomeIcon icon={faMoneyBill}></FontAwesomeIcon>
</div>
</animated.div >
My handleTokenClick
function looks as follows:
function handleTokenClick(tokenId) {
setActiveTokens(activeTokens => activeTokens.filter(token => token.id !== tokenId));
}
This obviously won't work, because I do not have any token.id
accessible from the tokenSprings
array. I scoured react spring docs, but I could not find a way to include an id that i'd specify in the tokenSprings
array for each element, that i can later reference in the jsx.
Is this possible? If not, how is it best to reference the actual tokenSpring
element which I want to remove from DOM?
There does appear to be a mixup in what the argument passed to the handleTokenClick
callback handler represents.
handleTokenClick
is coded to take a token id for filtering purposes
function handleTokenClick(tokenId) {
setActiveTokens(activeTokens => activeTokens.filter(
token => token.id !== tokenId
));
}
but is passed the mapped array index
tokenSprings.map((spring, index) => (
<animated.div
className="text-white text-2xl absolute cursor-pointer"
key={index}
style={spring}
>
<div onClick={() => handleTokenClick(index)}> // <-- index is passed
<FontAwesomeIcon icon={faMoneyBill} />
</div>
</animated.div>
))
Unless any of the element objects in the activeTokens
array happen to have id
properties that match an array index, it's likely that the filter condition will always evaluate false and no element is removed from the array.
To resolve you should refactor the handleTokenClick
handler to take an index and filter by the index value.
const handleTokenClick = (tokenIndex) {
setActiveTokens(activeTokens => activeTokens.filter(
(token, index) => index !== tokenIndex
));
}
clicked
property.Even if you are only updating a nested property of a specific element you necessarily need to create shallow copies of all state, and nested state, that is being updated for React's reconciliation process. Map the previous activeTokens
array to a new array, and then for the element object you want to update shallow copy it into a new object reference.
const handleTokenClick = (tokenIndex) {
setActiveTokens(activeTokens => activeTokens.map( // <-- new map reference
(token, index) => index == tokenIndex
? { // <-- new object reference
...token, // <-- shallow copy properties
clicked: true, // <-- updated property
}
: token
));
}