React beautiful animation reordering the list of items
Tags: webdev, react, javascript, css
Published: 2022-06-10 22:50:15


## Requirement

Suppose you have a list of items ( it could be a product list, user ranking, or whatever). You asked to implement the upvote with React. How to make it appealing?
In the case of this article, I would demo a list of products, and there will be a button to upvote a product item.

![Product list upvote](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dc54ozp23ptxewqar5g4.gif)

First few lines of code for a simple product list

![Simple product list](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/shmanlcnnnzrdxkl5q2d.png)

Assume we are going to fetch a list of products from the server and keep it in *state*. When the user clicks on the upvote button, we will increase the vote by one. 

## Initiative

- What is going to change when the list reorder? *The position of product item*

> getBoundingClientRect](https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect) WebAPI provide  us [DOMRect](https://developer.mozilla.org/en-US/docs/Web/API/DOMRect) of an element (which are `left`, `top`, `right`, `bottom`, `x`, `y`, `width`, and `height` properties)

Great. We have a product item's `top` and `left`. These properties are likely to change when the element goes up or down in the list.

- How to keep the previous position of items so we can add animation when there is a change? If we can have a reference of the product list, we can trigger some action whenever there is a change. Also, we can compare the difference between the previous position and the next position

> [createRef](https://reactjs.org/docs/react-api.html#reactcreateref) and [useRef](https://reactjs.org/docs/hooks-reference.html#useref) come in place to help. 

- We need to have a way to intervene in between state changes to add animation. What *React hooks* should we use here?

> [useLayoutEffect](https://reactjs.org/docs/hooks-reference.html#uselayouteffect) to read layout from the DOM and synchronously re-render. Updates scheduled inside useLayoutEffect will be flushed synchronously before the browser has a chance to paint.

Okay, now we know how we can access the state of layout in between

- The tricky thing here is how to make the *upvoting* smooth. [CSS Transitions](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Transitions/Using_CSS_transitions) came to my mind such a solution. 

> [transform](https://developer.mozilla.org/en-US/docs/Web/CSS/transform) and [transition](https://developer.mozilla.org/en-US/docs/Web/CSS/transition) go with [translate](https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/translate)

## Implementation

- Add ref to the product list with _createRef_

![createRef](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tqwyakghw0u53dl0e8m5.png)

- Create a custom hook to separate the logic 

![use custom hook](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8ca9mbj3iplze4khz84g.png)

![custom hook](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5jg63wt93bza1jkfzbnd.png)

- An object to store [DOMRect](https://developer.mozilla.org/en-US/docs/Web/API/DOMRect) of every single item and a boolean ref to not running animation on the first run

To keep track of the DOMRect, we use product id. The origin key must be a not-changed unique key so that the product id would be the best in this case.

- useLayoutEffect - **<u>the most important part</u>**

![useLayoutEffect](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6b6hj6baeyntukaioyds.png)

The logic here is to check every item on the list. 

`const previous = origins.current[key];` is the previous position of the item

`const next = child.getBoundingClientRect();` is the next position of the item after list reorder

This line of code is for checking the differences. If there is a difference, we applied animation to this item.

![Play animation](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ltgvq1tgx5nvmnwztf9c.png)

Using transform and transition in animation

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gxqhdsutrezhdwk1gm8p.png)

## Issue

I found an issue when scrolling a list. It causes the product element position to change. I added the code to update the item position when a scroll event trigger.


![useScroll](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4skvubv406hxnzui3dyn.png)

## Source code

You can find all source code here: ( with React 18, Typescript ) https://github.com/toantd90/react-flip.

- [faker](https://github.com/faker-js/faker) generate sample data
- [plop](https://github.com/plopjs/plop) for create a consistent templates for pages, components, etc


*Any comments would be appreciated!!!*