<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Toan Tran</title>
    <description>The latest articles on DEV Community by Toan Tran (@toantd90).</description>
    <link>https://dev.to/toantd90</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F37077%2F75ce7748-66af-4270-80f8-e1ecbc746db7.jpeg</url>
      <title>DEV Community: Toan Tran</title>
      <link>https://dev.to/toantd90</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/toantd90"/>
    <language>en</language>
    <item>
      <title>React beautiful animation reordering the list of items</title>
      <dc:creator>Toan Tran</dc:creator>
      <pubDate>Fri, 10 Jun 2022 22:50:15 +0000</pubDate>
      <link>https://dev.to/toantd90/react-beautiful-animation-reordering-the-list-of-items-1mbp</link>
      <guid>https://dev.to/toantd90/react-beautiful-animation-reordering-the-list-of-items-1mbp</guid>
      <description>&lt;h2&gt;
  
  
  Requirement
&lt;/h2&gt;

&lt;p&gt;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?&lt;br&gt;
In the case of this article, I would demo a list of products, and there will be a button to upvote a product item.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdc54ozp23ptxewqar5g4.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdc54ozp23ptxewqar5g4.gif" alt="Product list upvote"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;First few lines of code for a simple product list&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fshmanlcnnnzrdxkl5q2d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fshmanlcnnnzrdxkl5q2d.png" alt="Simple product list"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Assume we are going to fetch a list of products from the server and keep it in &lt;em&gt;state&lt;/em&gt;. When the user clicks on the upvote button, we will increase the vote by one. &lt;/p&gt;

&lt;h2&gt;
  
  
  Initiative
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;What is going to change when the list reorder? &lt;em&gt;The position of product item&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;getBoundingClientRect](&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect" rel="noopener noreferrer"&gt;https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect&lt;/a&gt;) WebAPI provide  us &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/DOMRect" rel="noopener noreferrer"&gt;DOMRect&lt;/a&gt; of an element (which are &lt;code&gt;left&lt;/code&gt;, &lt;code&gt;top&lt;/code&gt;, &lt;code&gt;right&lt;/code&gt;, &lt;code&gt;bottom&lt;/code&gt;, &lt;code&gt;x&lt;/code&gt;, &lt;code&gt;y&lt;/code&gt;, &lt;code&gt;width&lt;/code&gt;, and &lt;code&gt;height&lt;/code&gt; properties)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Great. We have a product item's &lt;code&gt;top&lt;/code&gt; and &lt;code&gt;left&lt;/code&gt;. These properties are likely to change when the element goes up or down in the list.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;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&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://reactjs.org/docs/react-api.html#reactcreateref" rel="noopener noreferrer"&gt;createRef&lt;/a&gt; and &lt;a href="https://reactjs.org/docs/hooks-reference.html#useref" rel="noopener noreferrer"&gt;useRef&lt;/a&gt; come in place to help. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;We need to have a way to intervene in between state changes to add animation. What &lt;em&gt;React hooks&lt;/em&gt; should we use here?&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://reactjs.org/docs/hooks-reference.html#uselayouteffect" rel="noopener noreferrer"&gt;useLayoutEffect&lt;/a&gt; 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.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Okay, now we know how we can access the state of layout in between&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The tricky thing here is how to make the &lt;em&gt;upvoting&lt;/em&gt; smooth. &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Transitions/Using_CSS_transitions" rel="noopener noreferrer"&gt;CSS Transitions&lt;/a&gt; came to my mind such a solution. &lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/transform" rel="noopener noreferrer"&gt;transform&lt;/a&gt; and &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/transition" rel="noopener noreferrer"&gt;transition&lt;/a&gt; go with &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/translate" rel="noopener noreferrer"&gt;translate&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Implementation
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Add ref to the product list with &lt;em&gt;createRef&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftqwyakghw0u53dl0e8m5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftqwyakghw0u53dl0e8m5.png" alt="createRef"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a custom hook to separate the logic &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8ca9mbj3iplze4khz84g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8ca9mbj3iplze4khz84g.png" alt="use custom hook"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5jg63wt93bza1jkfzbnd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5jg63wt93bza1jkfzbnd.png" alt="custom hook"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An object to store &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/DOMRect" rel="noopener noreferrer"&gt;DOMRect&lt;/a&gt; of every single item and a boolean ref to not running animation on the first run&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;useLayoutEffect - &lt;strong&gt;&lt;u&gt;the most important part&lt;/u&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6b6hj6baeyntukaioyds.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6b6hj6baeyntukaioyds.png" alt="useLayoutEffect"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The logic here is to check every item on the list. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;const previous = origins.current[key];&lt;/code&gt; is the previous position of the item&lt;/p&gt;

&lt;p&gt;&lt;code&gt;const next = child.getBoundingClientRect();&lt;/code&gt; is the next position of the item after list reorder&lt;/p&gt;

&lt;p&gt;This line of code is for checking the differences. If there is a difference, we applied animation to this item.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fltgvq1tgx5nvmnwztf9c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fltgvq1tgx5nvmnwztf9c.png" alt="Play animation"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Using transform and transition in animation&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgxqhdsutrezhdwk1gm8p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgxqhdsutrezhdwk1gm8p.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Issue
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4skvubv406hxnzui3dyn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4skvubv406hxnzui3dyn.png" alt="useScroll"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Source code
&lt;/h2&gt;

&lt;p&gt;You can find all source code here: ( with React 18, Typescript ) &lt;a href="https://github.com/toantd90/react-flip" rel="noopener noreferrer"&gt;https://github.com/toantd90/react-flip&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/faker-js/faker" rel="noopener noreferrer"&gt;faker&lt;/a&gt; generate sample data&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/plopjs/plop" rel="noopener noreferrer"&gt;plop&lt;/a&gt; for create a consistent templates for pages, components, etc&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Any comments would be appreciated!!!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>react</category>
      <category>javascript</category>
      <category>css</category>
    </item>
  </channel>
</rss>
