<?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>Forem: Andriy Chemerynskiy</title>
    <description>The latest articles on Forem by Andriy Chemerynskiy (@andrewchmr).</description>
    <link>https://forem.com/andrewchmr</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%2F397965%2F6396b785-97aa-4747-8f58-e575c3247bb8.jpg</url>
      <title>Forem: Andriy Chemerynskiy</title>
      <link>https://forem.com/andrewchmr</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/andrewchmr"/>
    <language>en</language>
    <item>
      <title>Experimenting with Three.js: Devlog #2</title>
      <dc:creator>Andriy Chemerynskiy</dc:creator>
      <pubDate>Sun, 26 Dec 2021 16:55:38 +0000</pubDate>
      <link>https://forem.com/andrewchmr/experimenting-with-threejs-devlog-2-9a8</link>
      <guid>https://forem.com/andrewchmr/experimenting-with-threejs-devlog-2-9a8</guid>
      <description>&lt;h2&gt;
  
  
  Before you read it
&lt;/h2&gt;

&lt;p&gt;This is not a tutorial and I am not a Three.js expert. I am going to document my learning journey and share it with you. If you find it useful then I would appreciate you leaving a comment or liking this post. Thanks and let's get started 😎&lt;/p&gt;




&lt;h2&gt;
  
  
  Controls and wall collision problem
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://dev.to/andrewchmr/experimenting-with-threejs-devlog-1-41k3"&gt;In the previous devlog&lt;/a&gt;, I added &lt;a href="https://threejs.org/docs/#examples/en/controls/PointerLockControls"&gt;PointerLockControls&lt;/a&gt; to my scene and assign movement on keypress (W, A, S, D keys). And as mentioned there is a problem that camera can pass throw walls.&lt;/p&gt;

&lt;p&gt;I didn't work with game dev or 3D before, so it was difficult for me to think about a solution for this. &lt;/p&gt;

&lt;p&gt;My initial thought was to create enough &lt;code&gt;if&lt;/code&gt; statements that would prevent the camera from passing throw walls (it was a super dummy idea 😅).&lt;/p&gt;

&lt;p&gt;After struggling with it for some time, I discovered that there is a much easier and better solution for this.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing physics
&lt;/h2&gt;

&lt;p&gt;I found this wonderful answer to my problem on Stackoverflow:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--woXJ8H4e--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qw34rig8pn6z3dwfqii4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--woXJ8H4e--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qw34rig8pn6z3dwfqii4.png" alt="Stackoverflow answer" width="800" height="84"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;It sounds way better than adding an infinite number of &lt;code&gt;if&lt;/code&gt; statements 🤔&lt;/p&gt;

&lt;p&gt;In easier words, it means that the viewer is a ball and if it will collide with the wall then physics will do its job 😱&lt;/p&gt;

&lt;p&gt;As Einstein once said:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xrYpIQ3d--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1n0kdb5b4g93cap3ix77.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xrYpIQ3d--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1n0kdb5b4g93cap3ix77.png" alt="Einstein quote" width="600" height="585"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;(or something like that).&lt;/p&gt;

&lt;h2&gt;
  
  
  Using use-cannon
&lt;/h2&gt;

&lt;p&gt;There is a great library for handling physics &lt;a href="https://github.com/pmndrs/use-cannon"&gt;use-cannon&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I defined walls using &lt;code&gt;useBox&lt;/code&gt; hook and sphere using &lt;code&gt;useSphere&lt;/code&gt;. Here is the result:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--g4pU79FM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1rfvbv0q5juf49kcnksb.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--g4pU79FM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1rfvbv0q5juf49kcnksb.gif" alt="useSphere and useBox example" width="800" height="412"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For debug purposes, I used &lt;a href="https://github.com/pmndrs/use-cannon#debug"&gt;Debug&lt;/a&gt; component&lt;/p&gt;

&lt;h2&gt;
  
  
  Applying camera to the sphere
&lt;/h2&gt;

&lt;p&gt;The last and most challenging part is to add the camera to the sphere.&lt;/p&gt;

&lt;p&gt;I was lucky to find this tutorial: &lt;a href="https://www.youtube.com/watch?v=ZnXKmODEFHA"&gt;https://www.youtube.com/watch?v=ZnXKmODEFHA&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I won't write in detail how it is done, because you can check it on your now 😉 But basically it just subscribing to sphere/ball position and setting the camera to that place.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TpIn62Si--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2f8fp9r1uhe4jsez52ws.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TpIn62Si--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2f8fp9r1uhe4jsez52ws.png" alt="Wall collision use-cannon" width="800" height="391"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;And it is done 🎉&lt;/p&gt;

&lt;p&gt;Here is how it looks all together:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vn5hvViu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xi62ui4kr00yu6g55v87.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vn5hvViu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xi62ui4kr00yu6g55v87.gif" alt="Wall collision threejs" width="800" height="412"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It looks like something basic on this video, but it felt soooo good to hit that wall and be stoped by the laws of physics 😍&lt;/p&gt;




&lt;p&gt;You might think that we are finished with controls and can move on to more advanced stuff. Not really...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://threejs.org/docs/#examples/en/controls/PointerLockControls"&gt;PointerLockControls&lt;/a&gt; won't work on mobile and we need to find another way of doing it. But I would talk about it in the next devlog 😉&lt;/p&gt;

&lt;p&gt;Thanks for reading! 🙌&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Experimenting with Three.js: Devlog #1</title>
      <dc:creator>Andriy Chemerynskiy</dc:creator>
      <pubDate>Sun, 19 Dec 2021 14:34:07 +0000</pubDate>
      <link>https://forem.com/andrewchmr/experimenting-with-threejs-devlog-1-41k3</link>
      <guid>https://forem.com/andrewchmr/experimenting-with-threejs-devlog-1-41k3</guid>
      <description>&lt;h2&gt;
  
  
  Before you read it
&lt;/h2&gt;

&lt;p&gt;This is not a tutorial and I am not a Three.js expert. I am going to document my learning journey and share it with you. If you find it useful then I would appreciate you leaving a comment or liking this post. Thanks and let's get started 😎&lt;/p&gt;




&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;My jaw dropped when I saw for the first time things done in &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API" rel="noopener noreferrer"&gt;WebGL&lt;/a&gt;. It started a revolution in the web world. However, it was super difficult to do anything in it, because it was too low-level (and I didn't have so much experience in programming 😅).&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%2F2v6ha8tgvt9w8krdxckt.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%2F2v6ha8tgvt9w8krdxckt.gif" alt="WebGL example"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Demo: &lt;a href="http://madebyevan.com/webgl-water/" rel="noopener noreferrer"&gt;http://madebyevan.com/webgl-water/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then Three.js enters the scene. Doing WebGL magic becomes easier and more cool applications appear online. &lt;br&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%2Fyaujrlm3p8wh80stugq1.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%2Fyaujrlm3p8wh80stugq1.gif" alt="Bruno Simon Website"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Website: &lt;a href="https://bruno-simon.com/" rel="noopener noreferrer"&gt;https://bruno-simon.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But still, it was not super easy to get something done in Three.js. Yes, it has great documentation and many examples, but in my opinion, for the average programmer, it was still difficult.&lt;/p&gt;
&lt;h2&gt;
  
  
  Let's check it again
&lt;/h2&gt;

&lt;p&gt;It is 2021. Many things have changed. More cool applications created, more examples, better docs 💪&lt;/p&gt;

&lt;p&gt;React is the number one js library. We have &lt;a href="https://github.com/pmndrs/react-three-fiber" rel="noopener noreferrer"&gt;react-three-fiber&lt;/a&gt; which allows us to use Three.js in React way (components, hooks, etc.). &lt;/p&gt;

&lt;p&gt;Awesome, now there is no need to write a lot of boilerplate low code to set the scene and have box rotating in it. &lt;/p&gt;
&lt;h2&gt;
  
  
  Learning sources
&lt;/h2&gt;

&lt;p&gt;Of course, I would recommend digging into Three.js, react-three-fiber documentation. &lt;/p&gt;

&lt;p&gt;If you need some more help with it then I would suggest you check:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.udemy.com/course/threejs-using-react/" rel="noopener noreferrer"&gt;Learn Three.js using React: Build a 3D Tesla Workshop 2021&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It shows the basics of Three.js and how it can be integrated with React-based web applications. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://threejs-journey.com/" rel="noopener noreferrer"&gt;Three.js Journey&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I don't think that it needs any explanation. Bruno Simon created lots of art pieces with WebGL and can share his knowledge with you.&lt;/p&gt;
&lt;h2&gt;
  
  
  Let's play with it
&lt;/h2&gt;

&lt;p&gt;After finishing &lt;a href="https://www.udemy.com/course/threejs-using-react/" rel="noopener noreferrer"&gt;Learn Three.js using React: Build a 3D Tesla Workshop 2021&lt;/a&gt;, I got fundamentals that would allow me to move on. Also, I learned how to use Three.js as a handbook and find everything I need.&lt;/p&gt;

&lt;p&gt;Soon I was able to create my first scene:&lt;br&gt;
&lt;iframe class="tweet-embed" id="tweet-1466117820526305283-812" src="https://platform.twitter.com/embed/Tweet.html?id=1466117820526305283"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1466117820526305283-812');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1466117820526305283&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;Here are used &lt;a href="https://threejs.org/docs/#examples/en/controls/PointerLockControls" rel="noopener noreferrer"&gt;PointerLockControls&lt;/a&gt;, but I do not handle wall collisions which mean that the camera can pass throw walls and objects 🤦‍♂️&lt;br&gt;
As well it is not real room texture but 6 walls that use box geometry 😄 &lt;/p&gt;

&lt;p&gt;But I am happy with what I have and it was easy to set it up 🙂&lt;/p&gt;




&lt;p&gt;In the next post, I will go more deeply into Three.js stuff and show you how I handled walls collision using a few tricks ✨&lt;/p&gt;

&lt;p&gt;Thanks for reading! 🙌&lt;/p&gt;

</description>
    </item>
    <item>
      <title>React + D3 Sunburst Chart ☀️</title>
      <dc:creator>Andriy Chemerynskiy</dc:creator>
      <pubDate>Tue, 21 Sep 2021 05:47:37 +0000</pubDate>
      <link>https://forem.com/andrewchmr/react-d3-sunburst-chart-3cpd</link>
      <guid>https://forem.com/andrewchmr/react-d3-sunburst-chart-3cpd</guid>
      <description>&lt;p&gt;In this post, I am going to show you how you can build a sunburst chart (or any chart) using React and D3.&lt;/p&gt;

&lt;h2&gt;
  
  
  Power of D3 and React 💪
&lt;/h2&gt;

&lt;p&gt;D3 is the king of data visualisation. It appeared around 10 years ago and there are still not so many libraries that can compete with it.&lt;/p&gt;

&lt;p&gt;What is more, most of JavaScript data visualisations libraries are built on top of D3, because it is low level and can be customized however you want.&lt;/p&gt;

&lt;h3&gt;
  
  
  React and D3 integration
&lt;/h3&gt;

&lt;p&gt;If you look into D3 code samples you might notice that it looks similar to... Yes, jQuery! It is not only visualization library but &lt;strong&gt;JavaScript library for manipulating documents based on data&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;There are 3 ways of integrating React and D3:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;D3-oriented approach: D3 manages the chart&lt;/li&gt;
&lt;li&gt;React-oriented approach: React manages the chart&lt;/li&gt;
&lt;li&gt;Hybrid approach: React for element creation, D3 for updates&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://www.createwithdata.com/integrating-react-and-d3" rel="noopener noreferrer"&gt;More info&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One of the key benefits of managing the chart using D3 is that we can easily add transitions, but in this tutorial, we would rely on a React-oriented approach as we would not need transitions (at least yet 🌚).&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%2F84xumh7igqm28kwfquc0.jpg" 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%2F84xumh7igqm28kwfquc0.jpg" alt="React.js + D3.js meme"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Why not use existing React based component libraries?
&lt;/h3&gt;

&lt;p&gt;Actually, you can (maybe you even should). There are many existing libraries with great API that would allow you creating different charts with low effort.&lt;/p&gt;

&lt;p&gt;However, sometimes you might get stuck if that library doesn't support the feature (or chart) you want.&lt;/p&gt;

&lt;p&gt;If you want to have full control over your visualisation then you should do it using D3.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building sunburst chart 👨🏼‍💻
&lt;/h2&gt;

&lt;p&gt;I know that many of you prefer to dive right into the code. &lt;/p&gt;

&lt;p&gt;Here is codesandbox with full code for this tutorial:&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://codesandbox.io/embed/sunburst-react-for-rendering-ioop1"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Finding D3 sunburst chart code
&lt;/h3&gt;

&lt;p&gt;Cool thing about D3 is that it has hundreds of visualisations with code for it. All you need to do is just google it:&lt;br&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%2Fbvlbxtf5e4m2yao9pezt.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%2Fbvlbxtf5e4m2yao9pezt.png" alt="Screen"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We would use the second link as it is a simpler example: &lt;a href="https://observablehq.com/@d3/sunburst" rel="noopener noreferrer"&gt;https://observablehq.com/@d3/sunburst&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%2Fa8waazplb0p0i8to9irt.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%2Fa8waazplb0p0i8to9irt.png" alt="screen"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This code might scare you in the beginning but it is okay. You don't have to understand every line of it. Our goal is to integrate it into React.&lt;/p&gt;
&lt;h3&gt;
  
  
  Basic setup
&lt;/h3&gt;

&lt;p&gt;Building our chart would start with adding svg ref:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;SunburstChart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;svgRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;useRef&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;SVGSVGElement&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;svg&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;svgRef&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;We are going to add &lt;code&gt;width&lt;/code&gt; (we will name it &lt;code&gt;SIZE&lt;/code&gt;) and &lt;code&gt;radius&lt;/code&gt; (we will name it &lt;code&gt;RADIUS&lt;/code&gt;) from code sample.&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%2Fd34yw10ucj2jlf28ejqn.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%2Fd34yw10ucj2jlf28ejqn.png" alt="screen"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;

&lt;/span&gt;&lt;span class="p"&gt;import React from "react";
&lt;/span&gt;&lt;span class="gi"&gt;+ const SIZE = 975;
+ const RADIUS = SIZE / 2;
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="p"&gt;export const SunburstChart = () =&amp;gt; {
&lt;/span&gt;  const svgRef = React.useRef&amp;lt;SVGSVGElement&amp;gt;(null);
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gd"&gt;-  return &amp;lt;svg ref={svgRef} /&amp;gt;;
&lt;/span&gt;&lt;span class="gi"&gt;+  return &amp;lt;svg width={SIZE} height={SIZE} ref={svgRef} /&amp;gt;;
&lt;/span&gt;};
&lt;span class="err"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;This chart uses json data and we are going to download it and add into our app.&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%2Frr7d724l07i3cy98coea.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%2Frr7d724l07i3cy98coea.png" alt="screen"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;

&lt;/span&gt;&lt;span class="p"&gt;import React from "react";
&lt;/span&gt;&lt;span class="gi"&gt;+ import data from "./data.json";
&lt;/span&gt;&lt;span class="p"&gt;const SIZE = 975;
const RADIUS = SIZE / 2;
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="p"&gt;export const SunburstChart = () =&amp;gt; {
&lt;/span&gt;  const svgRef = React.useRef&amp;lt;SVGSVGElement&amp;gt;(null);
&lt;span class="err"&gt;
&lt;/span&gt;  return &amp;lt;svg width={SIZE} height={SIZE} ref={svgRef} /&amp;gt;;
};
&lt;span class="err"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  D3 manages the chart
&lt;/h3&gt;

&lt;p&gt;Let's install &lt;code&gt;d3&lt;/code&gt; and &lt;code&gt;@types/d3&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

npm &lt;span class="nb"&gt;install &lt;/span&gt;d3 @types/d3


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;When installation is finished, we will put all chart setup code into &lt;code&gt;useEffect&lt;/code&gt; with little modifications&lt;br&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%2Fv7y056ub7czgrkt8kqti.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%2Fv7y056ub7czgrkt8kqti.png" alt="screen"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;

&lt;/span&gt;&lt;span class="p"&gt;import React from "react";
import data from "./data.json";
&lt;/span&gt;&lt;span class="gi"&gt;+ import * as d3 from "d3";
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="p"&gt;const SIZE = 975;
const RADIUS = SIZE / 2;
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="p"&gt;export const SunburstChart = () =&amp;gt; {
&lt;/span&gt;  const svgRef = React.useRef&amp;lt;SVGSVGElement&amp;gt;(null);
&lt;span class="gi"&gt;+  
+  React.useEffect(() =&amp;gt; {
+    const root = partition(data);
+
&lt;/span&gt;//   We already created svg element and will select its ref
&lt;span class="gd"&gt;-    const svg = d3.create("svg");
&lt;/span&gt;&lt;span class="gi"&gt;+    const svg = d3.select(svgRef.current);
+
+    svg
+      .append("g")
+      .attr("fill-opacity", 0.6)
+      .selectAll("path")
+      .data(root.descendants().filter((d) =&amp;gt; d.depth))
+      .join("path")
+      .attr("fill", (d) =&amp;gt; {
+        while (d.depth &amp;gt; 1) d = d.parent;
+        return color(d.data.name);
+      })
+      .attr("d", arc)
+      .append("title")
+      .text(
+        (d) =&amp;gt;
+          `${d
+            .ancestors()
+            .map((d) =&amp;gt; d.data.name)
+            .reverse()
+            .join("/")}\n${format(d.value)}`
+      );
+
+    svg
+      .append("g")
+      .attr("pointer-events", "none")
+      .attr("text-anchor", "middle")
+      .attr("font-size", 10)
+      .attr("font-family", "sans-serif")
+      .selectAll("text")
+      .data(
+        root
+          .descendants()
+          .filter((d) =&amp;gt; d.depth &amp;amp;&amp;amp; ((d.y0 + d.y1) / 2) * 
+          (d.x1 - d.x0) &amp;gt; 10)
+      )
+      .join("text")
+      .attr("transform", function (d) {
+        const x = (((d.x0 + d.x1) / 2) * 180) / Math.PI;
+        const y = (d.y0 + d.y1) / 2;
+        return `rotate(${
+          x - 90
+        }) translate(${y},0) rotate(${x &amp;lt; 180 ? 0 : 180})`;
+      })
+      .attr("dy", "0.35em")
+      .text((d) =&amp;gt; d.data.name);
+
&lt;/span&gt;//   We don't need to return svg node anymore
&lt;span class="gd"&gt;-    return svg.attr("viewBox", getAutoBox).node();
&lt;/span&gt;&lt;span class="gi"&gt;+    svg.attr("viewBox", getAutoBox);
+  }, []);
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;   return &amp;lt;svg width={SIZE} height={SIZE} ref={svgRef} /&amp;gt;;
};
&lt;span class="err"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Nice! Let's add missing functions:&lt;br&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%2Fpuph35bc17m3m8cnktfy.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%2Fpuph35bc17m3m8cnktfy.png" alt="screen"&gt;&lt;/a&gt;&lt;br&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%2Fegcp57cn5avg5smz2b1h.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%2Fegcp57cn5avg5smz2b1h.png" alt="screen"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;

&lt;/span&gt;...
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="p"&gt;export const SunburstChart = () =&amp;gt; {
&lt;/span&gt;  const svgRef = React.useRef&amp;lt;SVGSVGElement&amp;gt;(null);
&lt;span class="gi"&gt;+
+  const partition = (data) =&amp;gt;
+    d3.partition().size([2 * Math.PI, RADIUS])(
+      d3
+        .hierarchy(data)
+        .sum((d) =&amp;gt; d.value)
+        .sort((a, b) =&amp;gt; b.value - a.value)
+    );
+
+  const color = d3.scaleOrdinal(
+    d3.quantize(d3.interpolateRainbow,data.children.length+1)
+  );
+
+  const format = d3.format(",d");
+
+  const arc = d3
+    .arc()
+    .startAngle((d) =&amp;gt; d.x0)
+    .endAngle((d) =&amp;gt; d.x1)
+    .padAngle((d) =&amp;gt; Math.min((d.x1 - d.x0) / 2, 0.005))
+    .padRadius(RADIUS / 2)
+    .innerRadius((d) =&amp;gt; d.y0)
+    .outerRadius((d) =&amp;gt; d.y1 - 1);
+ 
&lt;/span&gt;// Custom autoBox function that calculates viewBox
// without doing DOM manipulations
&lt;span class="gd"&gt;-  function autoBox() {
-    document.body.appendChild(this);
-    const {x, y, width, height} = this.getBBox();
-    document.body.removeChild(this);
-    return [x, y, width, height];
-  }
&lt;/span&gt;&lt;span class="gi"&gt;+  const getAutoBox = () =&amp;gt; {
+    if (!svgRef.current) {
+      return "";
+    }
+
+    const { x, y, width, height } = svgRef.current.getBBox();
+
+    return [x, y, width, height].toString();
+  };
+
&lt;/span&gt;  React.useEffect(() =&amp;gt; {
    ...
&lt;span class="err"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;At this point, we should see our chart:&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://codesandbox.io/embed/kjee6"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Beautiful, isn't it? But it is not finished yet. We append chart elements using D3, but we don't handle updating it or cleaning it up. &lt;/p&gt;

&lt;p&gt;We can do it in &lt;code&gt;useEffect&lt;/code&gt; hook as well and let D3 manage it, but we will do it in React oriented way.&lt;/p&gt;

&lt;h3&gt;
  
  
  React manages the chart
&lt;/h3&gt;

&lt;p&gt;To have a better developing experience and avoid bugs we are going to fix types issues before we move on.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;

&lt;/span&gt;...
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+ interface Data {
+  name: string;
+  value?: number;
+ }
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="p"&gt;export const SunburstChart = () =&amp;gt; {
&lt;/span&gt;  const svgRef = React.useRef&amp;lt;SVGSVGElement&amp;gt;(null);
&lt;span class="err"&gt;
&lt;/span&gt;  const partition = (data: Data) =&amp;gt;
&lt;span class="gd"&gt;-    d3.partition().size([2 * Math.PI, RADIUS])(
&lt;/span&gt;&lt;span class="gi"&gt;+    d3.partition&amp;lt;Data&amp;gt;().size([2 * Math.PI, RADIUS])(
&lt;/span&gt;      d3
        .hierarchy(data)
        .sum((d) =&amp;gt; d.value)
        .sort((a, b) =&amp;gt; b.value - a.value)
    );
&lt;span class="err"&gt;
&lt;/span&gt;...
&lt;span class="err"&gt;
&lt;/span&gt;  const arc = d3
&lt;span class="gd"&gt;-   .arc()
&lt;/span&gt;&lt;span class="gi"&gt;+   .arc&amp;lt;d3.HierarchyRectangularNode&amp;lt;Data&amp;gt;&amp;gt;()
&lt;/span&gt;    .startAngle((d) =&amp;gt; d.x0)
    .endAngle((d) =&amp;gt; d.x1)
    .padAngle((d) =&amp;gt; Math.min((d.x1 - d.x0) / 2, 0.005))
    .padRadius(RADIUS / 2)
    .innerRadius((d) =&amp;gt; d.y0)
    .outerRadius((d) =&amp;gt; d.y1 - 1);
&lt;span class="err"&gt;
&lt;/span&gt;...
&lt;span class="err"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h4&gt;
  
  
  Remove append function and put everything in render
&lt;/h4&gt;

&lt;p&gt;This part is a bit difficult and might require a bit of D3 understanding. What I like to do is to inspect svg element throw DevTools and slowly move everything in render.&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%2Fup1d8ky4rkzc0vutukg1.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%2Fup1d8ky4rkzc0vutukg1.png" alt="screen"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see we have 2 groups. The first group keeps all paths and the other one keeps text elements.&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%2Fq5gmqqpjxcem11crn2z2.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%2Fq5gmqqpjxcem11crn2z2.png" alt="screen"&gt;&lt;/a&gt;&lt;br&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%2F9kvq71hwe6br3b1ips74.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%2F9kvq71hwe6br3b1ips74.png" alt="screen"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And we are going to repeat the same structure 😉&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;

&lt;/span&gt;...
&lt;span class="err"&gt;
&lt;/span&gt;  React.useEffect(() =&amp;gt; {
    const root = partition(data);
&lt;span class="err"&gt;
&lt;/span&gt;    const svg = d3.select(svgRef.current);
&lt;span class="gd"&gt;-
-    svg
-      .append("g")
-      .attr("fill-opacity", 0.6)
-      .selectAll("path")
-      .data(root.descendants().filter((d) =&amp;gt; d.depth))
-      .join("path")
-      .attr("fill", (d) =&amp;gt; {
-        while (d.depth &amp;gt; 1) d = d.parent;
-        return color(d.data.name);
-      })
-      .attr("d", arc)
-      .append("title")
-      .text(
-        (d) =&amp;gt;
-          `${d
-            .ancestors()
-            .map((d) =&amp;gt; d.data.name)
-            .reverse()
-            .join("/")}\n${format(d.value)}`
-      );
-
-    svg
-      .append("g")
-      .attr("pointer-events", "none")
-      .attr("text-anchor", "middle")
-      .attr("font-size", 10)
-      .attr("font-family", "sans-serif")
-      .selectAll("text")
-      .data(
-        root
-          .descendants()
-          .filter((d) =&amp;gt; d.depth &amp;amp;&amp;amp; ((d.y0 + d.y1) / 2) * 
-          (d.x1 - d.x0) &amp;gt; 10)
-      )
-      .join("text")
-      .attr("transform", function (d) {
-        const x = (((d.x0 + d.x1) / 2) * 180) / Math.PI;
-        const y = (d.y0 + d.y1) / 2;
-        return `rotate(${
-          x - 90
-        }) translate(${y},0) rotate(${x &amp;lt; 180 ? 0 : 180})`;
-      })
-      .attr("dy", "0.35em")
-      .text((d) =&amp;gt; d.data.name);
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;    svg.attr("viewBox", getAutoBox);
  }, []);
&lt;span class="gi"&gt;+
+ const getColor = (d: d3.HierarchyRectangularNode&amp;lt;Data&amp;gt;) =&amp;gt; {
+    while (d.depth &amp;gt; 1) d = d.parent;
+    return color(d.data.name);
+   };
+
+ const getTextTransform = 
+ (d: d3.HierarchyRectangularNode&amp;lt;Data&amp;gt;) =&amp;gt; {
+    const x = (((d.x0 + d.x1) / 2) * 180) / Math.PI;
+    const y = (d.y0 + d.y1) / 2;
+    return `rotate(${x - 90}) translate(${y},0) rotate(${x &amp;lt; + 180 ? 0 : 180})`;
+  };
+
+  const root = partition(data);
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;  return (
    &amp;lt;svg width={SIZE} height={SIZE} ref={svgRef}&amp;gt;
&lt;span class="gi"&gt;+      &amp;lt;g fillOpacity={0.6}&amp;gt;
+        {root
+          .descendants()
+          .filter((d) =&amp;gt; d.depth)
+          .map((d, i) =&amp;gt; (
+            &amp;lt;path 
+              key={`${d.data.name}-${i}`}
+              fill={getColor(d)}
+              d={arc(d)}
+             &amp;gt;
+              &amp;lt;text&amp;gt;
+                {d
+                  .ancestors()
+                  .map((d) =&amp;gt; d.data.name)
+                  .reverse()
+                  .join("/")}
+                \n${format(d.value)}
+              &amp;lt;/text&amp;gt;
+            &amp;lt;/path&amp;gt;
+          ))}
+      &amp;lt;/g&amp;gt;
+      &amp;lt;g
+        pointerEvents="none"
+        textAnchor="middle"
+        fontSize={10}
+        fontFamily="sans-serif"
+      &amp;gt;
+        {root
+          .descendants()
+          .filter((d) =&amp;gt; d.depth &amp;amp;&amp;amp; ((d.y0 + d.y1) / 2) * 
+          (d.x1 - d.x0) &amp;gt; 10)
+          .map((d, i) =&amp;gt; (
+            &amp;lt;text
+              key={`${d.data.name}-${i}`}
+              transform={getTextTransform(d)}
+              dy="0.35em"
+            &amp;gt;
+              {d.data.name}
+            &amp;lt;/text&amp;gt;
+          ))}
+      &amp;lt;/g&amp;gt;
&lt;/span&gt;    &amp;lt;/svg&amp;gt;
  );
};
&lt;span class="err"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Awesome, code looks much more readable! &lt;/p&gt;

&lt;p&gt;Last thing we are going to do it to pass viewBox value directly without using &lt;code&gt;attr()&lt;/code&gt; function.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;getAutoBox&lt;/code&gt; has to be run only one time and we are going to keep output of this function in the state. &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;


&lt;/span&gt;...
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="p"&gt;export const SunburstChart = () =&amp;gt; {
&lt;/span&gt;  const svgRef = React.useRef&amp;lt;SVGSVGElement&amp;gt;(null);
&lt;span class="gi"&gt;+ const [viewBox, setViewBox] = React.useState("0,0,0,0");
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;...
&lt;span class="gd"&gt;- React.useEffect(() =&amp;gt; {
-  const svg = d3.select(svgRef.current);
-  svg.attr("viewBox", getAutoBox);
- }, []);
&lt;/span&gt;&lt;span class="gi"&gt;+ React.useEffect(() =&amp;gt; {
+   setViewBox(getAutoBox());
+ }, []);
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;...
&lt;span class="err"&gt;
&lt;/span&gt;  return (
    &amp;lt;svg 
     width={SIZE}
     height={SIZE}
&lt;span class="gi"&gt;+    viewBox={viewBox}
&lt;/span&gt;     ref={svgRef}
     &amp;gt;
...
};
&lt;span class="err"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now we have chart fully managed by React with D3 calculations. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/3o7aCWJavAgtBzLWrS/giphy-downsized-large.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/3o7aCWJavAgtBzLWrS/giphy-downsized-large.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Demo + full code: &lt;a href="https://codesandbox.io/s/ioop1?file=/src/SunburstChart.tsx" rel="noopener noreferrer"&gt;https://codesandbox.io/s/ioop1?file=/src/SunburstChart.tsx&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;I hope this article was helpful and gave you a basic idea about integrating D3 charts with React 😉&lt;/p&gt;

&lt;p&gt;Make sure to follow me as I will post more content related to D3 and React.&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;

</description>
      <category>react</category>
      <category>d3</category>
      <category>chart</category>
      <category>javascript</category>
    </item>
    <item>
      <title>I made a platform where you can get free feedback on your website</title>
      <dc:creator>Andriy Chemerynskiy</dc:creator>
      <pubDate>Mon, 01 Mar 2021 05:11:56 +0000</pubDate>
      <link>https://forem.com/andrewchmr/i-made-a-platform-where-you-can-get-free-feedback-on-your-website-2jp2</link>
      <guid>https://forem.com/andrewchmr/i-made-a-platform-where-you-can-get-free-feedback-on-your-website-2jp2</guid>
      <description>&lt;h2&gt;
  
  
  Idea 💡
&lt;/h2&gt;

&lt;p&gt;I noticed that there are not so many places where people can get feedback, receive critics and suggestions on their website so they can improve it, validate and get ideas.&lt;/p&gt;

&lt;p&gt;People usually ask their friends for feedback or post it on different forums, but it's not a perfect solution.&lt;/p&gt;

&lt;p&gt;So I decided to build &lt;a href="https://roastmywebsite.net"&gt;Roast My Website&lt;/a&gt; that will help you to do it!&lt;/p&gt;

&lt;h2&gt;
  
  
  Building 🏗
&lt;/h2&gt;

&lt;p&gt;Having a full-time job, friends, and girlfriend that I want to spend time with, it took me 1 month to finish the beta version of this project.&lt;/p&gt;

&lt;p&gt;I chose technologies that I already know (or heard), so I would be confident implementing it👇&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://nextjs.org"&gt;Next.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://strapi.io"&gt;Strapi&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tailwindcss.com"&gt;Tailwind CSS&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I am happy with the results as the app is quite fast thanks to Next.js (frontend) and Strapi (backend).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--fSbfvEF4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r9o6x9h0wys8tr4vhynd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fSbfvEF4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r9o6x9h0wys8tr4vhynd.png" alt="image" width="800" height="293"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;All pages are statically generated which boosts up the performance. If you are looking for hybrid static &amp;amp; server rendering (and other yummy features) then Next.js is the right choice.&lt;/p&gt;

&lt;p&gt;Strapi saved me a lot of time as I wasn't writing backend from scratch and had a beautiful admin panel. It is definitely a time saver ⏳&lt;/p&gt;

&lt;p&gt;Tailwind CSS is a very good library for creating interfaces, and it's very difficult to mess something up. I used it for the first time and think that I might use it in my next projects.&lt;/p&gt;




&lt;p&gt;Here is a link to the project: &lt;a href="https://roastmywebsite.net"&gt;https://roastmywebsite.net&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let me know your thoughts about it 🙌&lt;/p&gt;

&lt;p&gt;P.S. Later I am going to write few posts about some tricks that I used while building it, so stay tuned 😉&lt;/p&gt;

</description>
    </item>
    <item>
      <title>React time input⏳</title>
      <dc:creator>Andriy Chemerynskiy</dc:creator>
      <pubDate>Sun, 22 Nov 2020 16:31:34 +0000</pubDate>
      <link>https://forem.com/andrewchmr/react-hh-mm-ss-time-input-cfl</link>
      <guid>https://forem.com/andrewchmr/react-hh-mm-ss-time-input-cfl</guid>
      <description>&lt;p&gt;At my work, I had to add input which is quite similar to YouTube's "Start at" input for sharing video:&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%2Fi%2Ffm1do8httzc8n2o89oqr.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%2Fi%2Ffm1do8httzc8n2o89oqr.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I am a pro level programmer, so the first thing I did was googling. However, all inputs that I found were different from what I needed. &lt;/p&gt;

&lt;p&gt;So I implemented my own clone of YouTube's "Start at" time input.&lt;/p&gt;




&lt;p&gt;Before I jump into the implementation part, here is the demo of what we are going to archive:&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%2Fi%2Fyffdx3fqdvihomong03f.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%2Fi%2Fyffdx3fqdvihomong03f.gif" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Behind the scenes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User types in&lt;/li&gt;
&lt;li&gt;When he finishes typing and clicks somewhere else &lt;code&gt;onBlur&lt;/code&gt; event is fired&lt;/li&gt;
&lt;li&gt;Getting seconds from input value (&lt;code&gt;getSecondsFromHHMMSS(value&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Converting those seconds back to hh:mm:ss format (&lt;code&gt;toHHMMSS(seconds)&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It may sound complicated now, but it will be clear a moment later 😉&lt;/p&gt;




&lt;p&gt;So let's start coding. &lt;/p&gt;

&lt;p&gt;Let's add a basic structure: &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;

&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;96&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;108&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;110&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0.15&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;40px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#606c6e&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;30px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;outline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="nb"&gt;transparent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="nb"&gt;transparent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;letter-spacing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;-0.4px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt; &lt;span class="m"&gt;18px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;200px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;"Helvetica Neue"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Helvetica&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Arial&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;sans-serif&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;30px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;text-align&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;TimeInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setValue&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0:00&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;onChange&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt; 
     &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
     &lt;span class="nx"&gt;onChange&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
     &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
     &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;ReactDOM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TimeInput&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We created &lt;code&gt;TimeInput&lt;/code&gt; component that has an initial value set to &lt;code&gt;O:00&lt;/code&gt; and we update the state on every change.&lt;/p&gt;

&lt;p&gt;Now we will add &lt;code&gt;onBlur&lt;/code&gt; handler:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;TimeInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setValue&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0:00&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="o"&gt;+&lt;/span&gt;  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;onBlur&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;seconds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;getSecondsFromHHMMSS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;toHHMMSS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;seconds&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;    &lt;span class="nf"&gt;setValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="p"&gt;...&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
     &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;
      &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="nx"&gt;onChange&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;+&lt;/span&gt;     &lt;span class="nx"&gt;onBlur&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;onBlur&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
     &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;ReactDOM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TimeInput&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;code&gt;onBlur&lt;/code&gt; function makes the same steps that I described earlier:&lt;/p&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Getting seconds from input value (getSecondsFromHHMMSS(value)&lt;/li&gt;
&lt;li&gt;Converting those seconds back to hh:mm:ss format (toHHMMSS(seconds))&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code&gt;Math.max(0, getSecondsFromHHMMSS(value))&lt;/code&gt; returns 0 if seconds are negative, so we don't have wrong values in our input.&lt;/p&gt;

&lt;p&gt;Now let's take a closer look at &lt;code&gt;getSecondsFromHHMMSS&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getSecondsFromHHMMSS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;str1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;str2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;str3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;val1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;str1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;val2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;str2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;val3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;str3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;isNaN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;val1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nf"&gt;isNaN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;val2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nf"&gt;isNaN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;val3&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// seconds&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;val1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;isNaN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;val1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;isNaN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;val2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nf"&gt;isNaN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;val3&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// minutes * 60 + seconds&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;val1&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;val2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;isNaN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;val1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;isNaN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;val2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;isNaN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;val3&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// hours * 60 * 60 + minutes * 60 + seconds&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;val1&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;val2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;val3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We split the input's value by ":". Then we grab 3 values from this array and convert them to numbers.&lt;/p&gt;

&lt;p&gt;Depending on the context &lt;code&gt;val1&lt;/code&gt;, &lt;code&gt;val2&lt;/code&gt;, &lt;code&gt;val3&lt;/code&gt; represent different values and handle those cases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Only seconds (eg. &lt;code&gt;10&lt;/code&gt;, &lt;code&gt;40&lt;/code&gt;, &lt;code&gt;70&lt;/code&gt; etc.) &lt;/li&gt;
&lt;li&gt;Minutes and seconds (eg. &lt;code&gt;1:20&lt;/code&gt;, &lt;code&gt;0:10&lt;/code&gt;, &lt;code&gt;14:40&lt;/code&gt; etc.)&lt;/li&gt;
&lt;li&gt;Hours, minutes, and seconds (eg. &lt;code&gt;1:12:40&lt;/code&gt;, &lt;code&gt;123:49:12&lt;/code&gt; etc.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Finally, we format seconds from &lt;code&gt;getSecondsFromHHMMSS&lt;/code&gt; back to &lt;code&gt;hh:mm:ss&lt;/code&gt; format:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;toHHMMSS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;secs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;secNum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;secs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hours&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;secNum&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;minutes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;secNum&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;seconds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;secNum&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;hours&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;minutes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;seconds&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;`0&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;00&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/^0/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;We get &lt;code&gt;hours&lt;/code&gt;, &lt;code&gt;minutes&lt;/code&gt;, &lt;code&gt;seconds&lt;/code&gt; from total seconds using simple math &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;map&lt;/code&gt; those values and if the value is less than 10 we add &lt;code&gt;0&lt;/code&gt; to it&lt;/li&gt;
&lt;li&gt;We don't want to show values like &lt;code&gt;00&lt;/code&gt; (exception is seconds), so we &lt;code&gt;filter&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;join&lt;/code&gt; our strings with ":"&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;replace&lt;/code&gt; leading zero&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And it's working 😎&lt;/p&gt;

&lt;p&gt;Codepen: &lt;a href="https://codepen.io/andrewchmr-the-vuer/pen/wvWLRVw" rel="noopener noreferrer"&gt;https://codepen.io/andrewchmr-the-vuer/pen/wvWLRVw&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;I hope this article was helpful and saved you the time of thinking about how to do this 😉&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;

</description>
      <category>react</category>
      <category>timeinput</category>
      <category>time</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Awesome animated cursor with React Hooks⚡️</title>
      <dc:creator>Andriy Chemerynskiy</dc:creator>
      <pubDate>Mon, 01 Jun 2020 12:36:24 +0000</pubDate>
      <link>https://forem.com/andrewchmr/awesome-animated-cursor-with-react-hooks-5ec3</link>
      <guid>https://forem.com/andrewchmr/awesome-animated-cursor-with-react-hooks-5ec3</guid>
      <description>&lt;p&gt;Don't you find built-in cursors kinda boring?🥱 Me too. So I built my own.&lt;/p&gt;




&lt;p&gt;Let's start by adding basic styles and logic to our cursor.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.cursor&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;40px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;40px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;#fefefe&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;fixed&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;translate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;-50%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;-50%&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;pointer-events&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;z-index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;9999&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;mix-blend-mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;difference&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;html&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#121212&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Cursor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cursor&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;ReactDOM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;App&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Cursor&lt;/span&gt;&lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;,
&lt;/span&gt;    &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we want to change our cursor's position based on mouse moves.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;const Cursor = () =&amp;gt; {
&lt;/span&gt;&lt;span class="gi"&gt;+   const [position, setPosition] = useState({x: 0, y: 0});
+
+   useEffect(() =&amp;gt; {
+       addEventListeners();
+       return () =&amp;gt; removeEventListeners();
+   }, []);
+
+   const addEventListeners = () =&amp;gt; {
+       document.addEventListener("mousemove", onMouseMove);
+   };
+
+   const removeEventListeners = () =&amp;gt; {
+       document.removeEventListener("mousemove", onMouseMove);
+   };
+
+   const onMouseMove = (e) =&amp;gt; {
+       setPosition({x: e.clientX, y: e.clientY});
+   };                                                               
+
&lt;/span&gt;&lt;span class="gd"&gt;-   return &amp;lt;div className="cursor"/&amp;gt;
&lt;/span&gt;&lt;span class="gi"&gt;+   return &amp;lt;div className="cursor"
+           style={{
+               left: `${position.x}px`,
+               top: `${position.y}px`
+           }}/&amp;gt;
&lt;/span&gt;}
&lt;span class="err"&gt;
&lt;/span&gt;...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When a component is mounted we add an event listener that handles &lt;code&gt;mousemove&lt;/code&gt; event and remove it when the component is going to unmount. In &lt;code&gt;onMouseMove&lt;/code&gt; function we set new cursor's position based on &lt;code&gt;e.clientX&lt;/code&gt; and &lt;code&gt;e.clientY&lt;/code&gt; properties.&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%2Fi.gyazo.com%2F885173cc0bbd64933e669ba4884698f2.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%2Fi.gyazo.com%2F885173cc0bbd64933e669ba4884698f2.gif" alt="Cursor GIF"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now our cursor reacts to mouse moves, but as you can see it doesn't hide when the mouse leaves the screen. So let's fix it!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;.cursor {
  ...
&lt;span class="gi"&gt;+ transition: all 150ms ease;
+ transition-property: opacity;
&lt;/span&gt;}
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+ .cursor--hidden {
+   opacity: 0;
+ }
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gi"&gt;+ import classNames from "classnames";
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="p"&gt;const Cursor = () =&amp;gt; {
&lt;/span&gt;    const [position, setPosition] = useState({x: 0, y: 0});
&lt;span class="gi"&gt;+   const [hidden, setHidden] = useState(false);
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;...
&lt;span class="err"&gt;
&lt;/span&gt;    const addEventListeners = () =&amp;gt; {
        document.addEventListener("mousemove", onMouseMove);
&lt;span class="gi"&gt;+       document.addEventListener("mouseenter", onMouseEnter);
+       document.addEventListener("mouseleave", onMouseLeave);
&lt;/span&gt;    };
&lt;span class="err"&gt;
&lt;/span&gt;    const removeEventListeners = () =&amp;gt; {
        document.removeEventListener("mousemove", onMouseMove);
&lt;span class="gi"&gt;+       document.removeEventListener("mouseenter", onMouseEnter);
+       document.removeEventListener("mouseleave", onMouseLeave);
&lt;/span&gt;    };
&lt;span class="gi"&gt;+
+   const onMouseLeave = () =&amp;gt; {
+       setHidden(true);
+   };
+
+   const onMouseEnter = () =&amp;gt; {
+       setHidden(false);
+   };
&lt;/span&gt;    ...
&lt;span class="gi"&gt;+
+   const cursorClasses = classNames(
+       'cursor',
+       {
+           'cursor--hidden': hidden
+       }
+   );                                                             
+
&lt;/span&gt;&lt;span class="gd"&gt;-   return &amp;lt;div className="cursor"
&lt;/span&gt;&lt;span class="gi"&gt;+   return &amp;lt;div className={cursorClasses}
&lt;/span&gt;            style={{
                left: `${position.x}px`,
                top: `${position.y}px`
            }}/&amp;gt;
}
&lt;span class="err"&gt;
&lt;/span&gt;...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, I add &lt;code&gt;mouseleave&lt;/code&gt; and &lt;code&gt;mouseenter&lt;/code&gt; handler. When the mouse enters the screen's &lt;code&gt;opacity&lt;/code&gt; becomes &lt;code&gt;1&lt;/code&gt; and when leaves - equals to &lt;code&gt;0&lt;/code&gt;. Additionally, I add &lt;code&gt;classnames&lt;/code&gt; library which is a simple utility for conditionally joining classNames together.&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%2Fi.gyazo.com%2Fce34c4738130e0825c6857ad7c59bc6e.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%2Fi.gyazo.com%2Fce34c4738130e0825c6857ad7c59bc6e.gif" alt="Cursor GIF"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now it looks way better, but let's add some more stuff! &lt;/p&gt;

&lt;p&gt;Let's add click animation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;.cursor {
  ...
&lt;span class="gd"&gt;- transition-property: opacity;
&lt;/span&gt;&lt;span class="gi"&gt;+ transition-property:  opacity, background-color, transform, mix-blend-mode;
&lt;/span&gt;  ...
}
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+ .cursor--clicked {
+   transform: translate(-50%, -50%) scale(0.9);
+   background-color: #fefefe;
+ }
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;const Cursor = () =&amp;gt; {
&lt;/span&gt;    ...
&lt;span class="gi"&gt;+   const [clicked, setClicked] = useState(false);
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;    const addEventListeners = () =&amp;gt; {
        ...
&lt;span class="gi"&gt;+       document.addEventListener("mousedown", onMouseDown);
+       document.addEventListener("mouseup", onMouseUp);
&lt;/span&gt;    };
&lt;span class="err"&gt;
&lt;/span&gt;    const removeEventListeners = () =&amp;gt; {
        ...
&lt;span class="gi"&gt;+       document.removeEventListener("mousedown", onMouseDown);
+       document.removeEventListener("mouseup", onMouseUp);
&lt;/span&gt;    };
&lt;span class="gi"&gt;+
+   const onMouseDown = () =&amp;gt; {
+       setClicked(true);
+   };
+
+   const onMouseUp = () =&amp;gt; {
+       setClicked(false);
+   };
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;    ...
&lt;span class="err"&gt;
&lt;/span&gt;    const cursorClasses = classNames(
        'cursor',
        {
&lt;span class="gi"&gt;+           'cursor--clicked': clicked,
&lt;/span&gt;            'cursor--hidden': hidden
        }
    );
&lt;span class="err"&gt;
&lt;/span&gt;...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mouse clicks are handled by &lt;code&gt;mousedown&lt;/code&gt; and &lt;code&gt;mouseup&lt;/code&gt; event. When the mouse is clicked, the cursor's scale changes to &lt;code&gt;0.9&lt;/code&gt; and background to &lt;code&gt;#fefefe&lt;/code&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%2Fi.gyazo.com%2F7032996b0b10b969c8d91845e6e02d6f.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%2Fi.gyazo.com%2F7032996b0b10b969c8d91845e6e02d6f.gif" alt="Cursor GIF"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's move on to our final animation!&lt;/p&gt;

&lt;p&gt;Now we will add some effects when links have hovered.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;...
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+ .cursor--link-hovered {
+   transform: translate(-50%, -50%) scale(1.25);
+   background-color: #fefefe;
+ }
+
+ a {
+   text-decoration: underline;
+   color: #fefefe;
+ }
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;const Cursor = () =&amp;gt; {
&lt;/span&gt;    ...
&lt;span class="gi"&gt;+   const [linkHovered, setLinkHovered] = useState(false);
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;    useEffect(() =&amp;gt; {
       addEventListeners();
&lt;span class="gi"&gt;+      handleLinkHoverEvents();
&lt;/span&gt;       return () =&amp;gt; removeEventListeners();
    }, []);
&lt;span class="gi"&gt;+   
&lt;/span&gt;    ...
&lt;span class="gi"&gt;+
+   const handleLinkHoverEvents = () =&amp;gt; {
+       document.querySelectorAll("a").forEach(el =&amp;gt; {
+           el.addEventListener("mouseover", () =&amp;gt; setLinkHovered(true));
+           el.addEventListener("mouseout", () =&amp;gt; setLinkHovered(false));
+       });
+   };
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;    const cursorClasses = classNames(
        'cursor',
        {
            'cursor--clicked': clicked,
            'cursor--hidden': hidden,
&lt;span class="gi"&gt;+           'cursor--link-hovered': linkHovered
&lt;/span&gt;        }
    );
    ...
}
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="p"&gt;ReactDOM.render(
&lt;/span&gt;    &amp;lt;div className="App"&amp;gt;
&lt;span class="gi"&gt;+       &amp;lt;a&amp;gt;This is a link&amp;lt;/a&amp;gt;
&lt;/span&gt;        &amp;lt;Cursor/&amp;gt;
    &amp;lt;/div&amp;gt;,
    document.getElementById('root')
);
&lt;span class="err"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When a component is mounted, &lt;code&gt;handleLinkHoverEvents&lt;/code&gt; add event listeners to all link elements. When a link hovers, &lt;code&gt;cursor--link-hovered&lt;/code&gt; class is added.&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%2Fi.gyazo.com%2F738c48ba452f32d38d80df26816fa03c.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%2Fi.gyazo.com%2F738c48ba452f32d38d80df26816fa03c.gif" alt="Cursor GIF"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the final step, we will not render &lt;code&gt;&amp;lt;Cursor/&amp;gt;&lt;/code&gt; on mobile/touch devices.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gi"&gt;+ const isMobile = () =&amp;gt; {
+     const ua = navigator.userAgent;
+     return /Android|Mobi/i.test(ua);
+ };
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="p"&gt;const Cursor = () =&amp;gt; {
&lt;/span&gt;&lt;span class="gi"&gt;+   if (typeof navigator !== 'undefined' &amp;amp;&amp;amp; isMobile()) return null;
&lt;/span&gt;    ...
}
&lt;span class="err"&gt;
&lt;/span&gt;...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And we are done! Here is a full codepen example:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/andrewchmr-the-vuer/embed/GRZjbbB?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;




&lt;p&gt;Adding custom cursor animation is not as difficult as it seems to be. I hope that this article will give you a basic idea of what you can do to customize your own cursor.&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>reacthooks</category>
      <category>cursor</category>
    </item>
  </channel>
</rss>
