Card Guessing game with React part 10
Now that we have come a long way and our functional requirement is almost complete, it's time for decoration. First of all, I notice that the transition between the selection of the columns has no animation at all. It would be easier for a user to keep track of their chosen card if it goes to the next position with a transition animation. You will see what I mean in a minute (or an hour, depending on your skill :P ).
Animating the position of a DOM elements is not easy especially if it has to go to a specific new position. Which is why we need to make use of a React library called react-flip-move
.
Install react-flip-move
.
npm i react-flip-move
If you face an issue related to "TypeError [ERRINVALIDARG_TYPE]:", you can fix it by changing the react-scripts
version in the package.json
as it seems to be the issue caused by the older version.
"react-scripts": "^3.4.0"
After installing, import the library to App.js
to use it.
import FlipMove from 'react-flip-move'
To use it we can wrap FlipMove
around our Card
grid as we can see from the github page of the library.
Let's follow that in our App.js
and try the app to see if it works.
<FlipMove> <div className="grid-container">{this.renderGrid()}</div>
</FlipMove>
It didn't work. Why? There are two reasons.
1. No Unique key
In the Gotchas section of react-flip-move
github, it says we need a unique key
property for all the children. Without it, the library won't be able to properly get the position of the component we want to animate.
To fix this we need to pass num
as the key
instead of index
as key
.
renderGrid = () => {
return this.state.numbers.map((num, i) => {
return (
<Card key={num} number={num} /> )
})
}
Why I changed from key={i}
to key={num}
? That's because using i or (index)
as key
when mapping an array is a bad practice. Let me elaborate.
Let's assume, we have just 3 cards [0,1,2] and there is no randomization. After mapping with the index
we will get the for 3 cards are as follow.
map
will put the index
in the order of the array. The first number will get key={0}
, second one will get key={1}
and the third, key={2}
.
Say we have randomize the cards for the next step and the cards now become [2,1,0]. Then we will get this.
The key
that is supposed to stay attached (unique) to the number is not working. It is assigning the key
depending on the position of the element in the array, not the element itself. By using index
as key
property, React (or in our case react-flip-move
) cannot properly identify the components that have been changed/moved.
2. Passing key
to the wrong component
In our code, we are passing key
to Card
component. It is wrong because Card
is our custom component and it does not take in key
as a props
. To fix this we need to wrap an empty div
with key={num}
around Card
component when mapping.
<div key={num}> <Card number={num} />
</div>
Next we need to remove the div
with class name grid-container
that wraps {this.renderGrid()}
as FlipMove
will now act as the wrapper. Don't forget to give FlipMove
the className
of grid-container
to preserve our grid layout.
<FlipMove className="grid-container">{this.renderGrid()}</FlipMove>
.
Look at that! :D Just like how we wanted. But let's do some minor tweaks. It looks good but the animation feels unnatural. We will add some stagger delay and play with duration.
After playing round with react-flip-move
API from https://github.com/joshwcomeau/react-flip-move/blob/master/documentation/api_reference.md , I found the following attributes give good result. Feel free to play around with it to your liking.
<FlipMove
easing="ease-in-out" duration={500} staggerDelayBy={20} className="grid-container"
>
And the end result is perfect.
That's it for today. Feel free to let me know in the comments if you see there is room for improvement.
Title Photo by Clifford Photography`on Unsplash.