<?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: Nikita Popov</title>
    <description>The latest articles on DEV Community by Nikita Popov (@losnikitos).</description>
    <link>https://dev.to/losnikitos</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%2F1001700%2F3ad4696b-c22c-4097-be30-69500ff06da7.jpg</url>
      <title>DEV Community: Nikita Popov</title>
      <link>https://dev.to/losnikitos</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/losnikitos"/>
    <language>en</language>
    <item>
      <title>Three Ways to Make an Async Request in JavaScript: XHR, Promise, Async/Await</title>
      <dc:creator>Nikita Popov</dc:creator>
      <pubDate>Sat, 11 Mar 2023 22:10:37 +0000</pubDate>
      <link>https://dev.to/losnikitos/three-ways-to-make-an-async-request-in-javascript-xhr-promise-asyncawait-1bff</link>
      <guid>https://dev.to/losnikitos/three-ways-to-make-an-async-request-in-javascript-xhr-promise-asyncawait-1bff</guid>
      <description>&lt;p&gt;Asynchronous programming is a crucial part of modern-day JavaScript development. It allows you to make requests to servers, access databases, and manipulate the DOM without blocking the main thread of execution.&lt;/p&gt;

&lt;p&gt;The way we develop code in 2023 is very different from how it was done in the 2000s. We now have access to promises, async/await, and the fetch API, whereas back then we were using XHR and callbacks. If you were handed a legacy frontend project, this article can help you understand how it was done in the past so that you can rewrite it into a modern codebase if necessary.&lt;/p&gt;

&lt;p&gt;In this article, we will build a simple program that fetches JSON from a URL and prints the result to the console. We will use code patterns from different eras of JavaScript. &lt;/p&gt;

&lt;h3&gt;
  
  
  The 2005 way: XMLHttpRequest (XHR)
&lt;/h3&gt;

&lt;p&gt;XMLHttpRequest (XHR) is an API in the form of an object that is used to transfer data between the web browser and a server. It is the oldest way to make an asynchronous request in JavaScript. XHR provides a way to make HTTP requests to the server and get responses without having to reload the entire page.&lt;/p&gt;

&lt;p&gt;Here is an example of how to make an XHR request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;xhr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;XMLHttpRequest&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;xhr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GET&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://jsonplaceholder.typicode.com/todos/1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;xhr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;responseText&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error&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="p"&gt;};&lt;/span&gt;
&lt;span class="nx"&gt;xhr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;send&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or maybe you were using jQuery? It was a popular JavaScript library that simplified HTML document traversing, event handling, animating, and Ajax interactions. This is what the same request will look like in jQuery:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getJSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://jsonplaceholder.typicode.com/todos/1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&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;h3&gt;
  
  
  The 2015 way: Fetch and promises
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Fetch&lt;/strong&gt; is a newer and more modern technology that replaces XHR, or XMLHttpRequest. It is built directly into modern web browsers, which means that it is easier to use and requires less overhead than XHR. It uses &lt;strong&gt;Promises&lt;/strong&gt; instead of callbacks, which makes it easier to manage asynchronous calls without getting bogged down in complex code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Promise&lt;/strong&gt; is an object in JavaScript that represents the eventual completion (or failure) of an asynchronous operation and its resulting value. It allows you to write asynchronous code that is more readable and maintainable by chaining together methods like &lt;code&gt;then()&lt;/code&gt; and &lt;code&gt;catch()&lt;/code&gt; to handle the result of a Promise.&lt;/p&gt;

&lt;p&gt;Here is an example of how to make an async request using Fetch and Promises:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://jsonplaceholder.typicode.com/todos/1&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="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&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;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Network response was not ok&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&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;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The 2023 way: Async/await
&lt;/h3&gt;

&lt;p&gt;Async/await is a modern way of writing asynchronous code that is built on top of Promises. It allows you to write asynchronous code that looks like synchronous code. Here is an example of how to make an async request using Async/await:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;getData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&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;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;https://example.com/api/data&amp;gt;&lt;/span&gt;&lt;span class="dl"&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="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Network response was not ok&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&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;getData&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that we have to wrap the code in a &lt;code&gt;getData&lt;/code&gt; function as &lt;code&gt;await&lt;/code&gt; statement cannot be called in top level code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;We explored three different ways to make asynchronous requests in JavaScript. When possible, avoid using legacy code and instead utilize new language features. However, make sure that the target browsers support these features. You can check &lt;a href="http://caniuse.com/"&gt;caniuse.com&lt;/a&gt; if you're unsure.&lt;/p&gt;

&lt;p&gt;Thank you for reading! I hope this article has been helpful to you. Happy coding!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>promise</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Debounce onChange callback using a custom React hook</title>
      <dc:creator>Nikita Popov</dc:creator>
      <pubDate>Sat, 11 Feb 2023 12:52:34 +0000</pubDate>
      <link>https://dev.to/losnikitos/debounce-onchange-callback-using-a-custom-react-hook-1667</link>
      <guid>https://dev.to/losnikitos/debounce-onchange-callback-using-a-custom-react-hook-1667</guid>
      <description>&lt;h1&gt;
  
  
  Intro
&lt;/h1&gt;

&lt;p&gt;Let's say you have a search component with a suggest. Suggest items are fetched as you type. You don't want to fetch items on every keystroke, you want to fetch when the user stops typing. How to do this? You probably want to use a technique called 'debouncing'.&lt;/p&gt;

&lt;h1&gt;
  
  
  What is debouncing?
&lt;/h1&gt;

&lt;p&gt;Debouncing is a technique to limit the rate at which a function is being called. It is commonly used to improve performance and user experience when working with events that trigger rapid function calls.&lt;/p&gt;

&lt;p&gt;One common example of when debouncing is needed is when dealing with user inputs, such as typing in a search field. If you were to make a server request for every character that the user types, it could lead to a large number of unnecessary requests and slow down the app.&lt;/p&gt;

&lt;p&gt;In this article I will explain how to reduce number of network requests using debouncing. We'll also extract debouncing logic into a custom React hook which can be reused. &lt;/p&gt;

&lt;h1&gt;
  
  
  Example
&lt;/h1&gt;

&lt;p&gt;Let's take a look at a practical example. Below there's a form for searching a movie by its name. Movie names are fetched from server (in this example we'll be using a mock instead of a real server, but it doesn't make much difference). Try searching 'godfather' in the input below, notice the output in the console:&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://codesandbox.io/embed/step-1-no-debounce-1uoorx"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;As you see, the app is making a server request after each keystroke:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fgn1wmrn5bqfsrbwsmhvm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fgn1wmrn5bqfsrbwsmhvm.png" alt="Image description" width="800" height="520"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's see how we can improve this!&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: add &lt;code&gt;setTimeout&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Let's take a look at our Input component. Currently it renders the &lt;code&gt;value&lt;/code&gt; provided by it's parent component, and calls &lt;code&gt;onChange&lt;/code&gt; every time user changes the value:&lt;br&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;function&lt;/span&gt; &lt;span class="nf"&gt;Input&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;onChange&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&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="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;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&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="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 don't want to call &lt;code&gt;onChange&lt;/code&gt; instantly, let's try to wrap it in a &lt;code&gt;setTimeout&lt;/code&gt;:&lt;br&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;function&lt;/span&gt; &lt;span class="nf"&gt;Input&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;onChange&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// This callback calls 'onChange' after a timeout&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;debouncedOnChange&lt;/span&gt; &lt;span class="o"&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;onChange&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="mi"&gt;1000&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;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="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;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;debouncedOnChange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&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="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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Give it a try:&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://codesandbox.io/embed/step-2-settimeout-j6q0vn"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;This clearly adds a delay, but doesn't work as expected! Notice how user input is delayed. Looks like &lt;code&gt;onChange&lt;/code&gt; should be called with a delay, but input value should update instantly, without an delay. How can we achieve this?&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: add local state
&lt;/h2&gt;

&lt;p&gt;We can add local state to our component to store current user input. We'll update local state instantly, and call &lt;code&gt;onChange&lt;/code&gt; with a delay. Here's what it might look like:&lt;br&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;function&lt;/span&gt; &lt;span class="nf"&gt;Input&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;onChange&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Store user input in local state. Initial value is provided by parent component&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;localValue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setLocalValue&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;debouncedOnChange&lt;/span&gt; &lt;span class="o"&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Set local value instantly&lt;/span&gt;
    &lt;span class="nf"&gt;setLocalValue&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="c1"&gt;// Set parent value after a delay&lt;/span&gt;
    &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;onChange&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="mi"&gt;1000&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;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;localValue&lt;/span&gt;&lt;span class="p"&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;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;debouncedOnChange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&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="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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;iframe src="https://codesandbox.io/embed/step-3-settimeout-state-ckzqgn"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;It works great! But looks like it still makes a network request on every key stroke, just the requests are delayed now. Let's fix this!&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: add clearTimeout
&lt;/h2&gt;

&lt;p&gt;If user changes input during the timeout, we want to cancel scheduled &lt;code&gt;onChange&lt;/code&gt; call and schedule another one, with a new input value. Each time we call &lt;code&gt;setTimeout&lt;/code&gt;, it returns a unique number called timeout id. We can pass this number to &lt;code&gt;clearTimeout&lt;/code&gt; to cancel the scheduled timeout and then schedule a new one. To store timeout id we can use &lt;code&gt;useRef&lt;/code&gt; hook. Here's what it can look like:&lt;br&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;function&lt;/span&gt; &lt;span class="nf"&gt;Input&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;onChange&lt;/span&gt; &lt;span class="p"&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;localValue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setLocalValue&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&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="c1"&gt;// Stores reference to timeout id&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;timeoutRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRef&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;debouncedOnChange&lt;/span&gt; &lt;span class="o"&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Clear timeout if there was one pending&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;timeoutRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;clearTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;timeoutRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Set local value instantly&lt;/span&gt;
    &lt;span class="nf"&gt;setLocalValue&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="c1"&gt;// Schedule `onChange` call and store timeout id in the ref&lt;/span&gt;
    &lt;span class="nx"&gt;timeoutRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;onChange&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="mi"&gt;1000&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;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;localValue&lt;/span&gt;&lt;span class="p"&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;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;debouncedOnChange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&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="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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;iframe src="https://codesandbox.io/embed/step-4-cleartimeout-tuvq2i"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Brilliant! It does the job!&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: extracting to a custom hook
&lt;/h2&gt;

&lt;p&gt;Notice how bloated &lt;code&gt;Input&lt;/code&gt; component has become. Let's extract debounce related logic to a custom hook. This way our component code will be shorter and easier to maintain, and debounce logic can be reused. Here's what it might look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// This custom hook can transform any callback to a debounced version&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;useDebounce&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;duration&lt;/span&gt;&lt;span class="p"&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;timeoutRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRef&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;onEdit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCallback&lt;/span&gt;&lt;span class="p"&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="o"&gt;=&amp;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="nx"&gt;timeoutRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;clearTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;timeoutRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nx"&gt;timeoutRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;onChange&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;duration&lt;/span&gt;&lt;span class="p"&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;duration&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="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;onEdit&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Input&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;onChange&lt;/span&gt; &lt;span class="p"&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;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setText&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&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="c1"&gt;// Custom hook accepts two arguments: original onChange callback, and debounce delay. Its return value is a new, debounced callback&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;debouncedOnChange&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useDebounce&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="mi"&gt;1000&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;onEdit&lt;/span&gt; &lt;span class="o"&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setText&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="nf"&gt;debouncedOnChange&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="k"&gt;return&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;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;text&lt;/span&gt;&lt;span class="p"&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;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;onEdit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&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="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;Here you can play around with the final version&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://codesandbox.io/embed/step-5-custom-hook-fkltlv"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;We have introduced debouncing to our component to limit number of network requests. We've 'packed' debounce code into a custom hook, so that it can be reused elsewhere.&lt;/p&gt;

&lt;p&gt;Thank you for following my tutorial!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Cover photo by &lt;a href="https://unsplash.com/@wgzhai?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Wengang Zhai&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/MJ_0PxIuquI?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>announcement</category>
      <category>devto</category>
      <category>web3</category>
      <category>crypto</category>
    </item>
    <item>
      <title>Project tracker in Google Spreadsheet</title>
      <dc:creator>Nikita Popov</dc:creator>
      <pubDate>Wed, 04 Jan 2023 15:36:43 +0000</pubDate>
      <link>https://dev.to/losnikitos/project-tracker-in-google-spreadsheet-11n1</link>
      <guid>https://dev.to/losnikitos/project-tracker-in-google-spreadsheet-11n1</guid>
      <description>&lt;h3&gt;
  
  
  Intro
&lt;/h3&gt;

&lt;p&gt;Hey, how is it going? I'm an engineer in a tech company. We've recently finished a project involving &lt;strong&gt;4 engineers for 4 months&lt;/strong&gt;. To track our progress we were using a &lt;strong&gt;custom project tracker&lt;/strong&gt; in Google Spreadsheet. It worked great for us, so I want to share how the tracker works and what problems it helped us solve. Maybe you can use a similar approach in your next project.&lt;/p&gt;

&lt;h3&gt;
  
  
  What problem we were solving
&lt;/h3&gt;

&lt;p&gt;We were looking for a project tracking tool which would&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Let us &lt;strong&gt;plan work&lt;/strong&gt; for a few engineers over multiple weeks, taking multiple external events into consideration (days off, oncall, code freeze, other projects) and allow balancing work load between engineers.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Let us &lt;strong&gt;see project status&lt;/strong&gt; at a glance. It should be easy to answer questions like ‘who is working on workstream X?’, ‘are we on track?’, ‘what have we completed in last few weeks?’, ‘when will we deliver feature X?’, ‘when do we plan to be code complete/in QA/launch?’.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Be &lt;strong&gt;flexible&lt;/strong&gt; enough to support ad-hoc manipulations, e.g. sort tasks, assign priorities, hide tasks or dates, highlight a date, highlight risky tasks, optional tasks etc.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Be &lt;strong&gt;efficient in bulk&lt;/strong&gt; operations. It should be easy to reassign multiple tasks, adjust estimation or start/end date, split  task into subtasks.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Flexibility is probably the requirement which is hardest to achieve. Ideally we wanted a tool which was closer to a whiteboard, rather than to a traditional project tracker. That's why we didn't go with Asana or MS Project or similar tools.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fl8prerfx11zv42kpako7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fl8prerfx11zv42kpako7.png" alt="Image" width="800" height="150"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A spreadsheet sits nicely in the middle on this axis, so we took it and have built a custom solution on top of it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Our tracker
&lt;/h3&gt;

&lt;p&gt;The tracker fits one spreadsheet:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Ft2jri91ejw1m36rf8ikr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Ft2jri91ejw1m36rf8ikr.png" alt="Image" width="800" height="608"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It has four areas:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fs9erj3nwvd5z7rdplw2y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fs9erj3nwvd5z7rdplw2y.png" alt="Image" width="800" height="522"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s take a closer look at them.&lt;/p&gt;

&lt;h4&gt;
  
  
  Tasks
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fli5k4jvg6vztj0gjljvq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fli5k4jvg6vztj0gjljvq.png" alt="Image" width="751" height="699"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each row represents a task. A task has a &lt;strong&gt;name&lt;/strong&gt; and a &lt;strong&gt;description&lt;/strong&gt;, an estimation in &lt;strong&gt;weeks&lt;/strong&gt;, and an &lt;strong&gt;owner&lt;/strong&gt;. Owner is indicated by the initials (for example NP for Nikita Popov). Tasks are grouped into &lt;strong&gt;workstreams&lt;/strong&gt;, an arbitrary way to group tasks which helps collocate similar tasks together.&lt;/p&gt;

&lt;h4&gt;
  
  
  Timeline
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fhkr1mismvu4cwxxe5zwr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fhkr1mismvu4cwxxe5zwr.png" alt="Image" width="800" height="629"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One cell in the timeline represents one week. Vertical red line indicates the current week. If an engineer is working on a task during a week, we put their initials into the corresponding cell. For example, in the first row an engineer named B.O. is working on a task estimated at 4 weeks, and he is 3 weeks into it:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fwiyc2makttvptrymxndp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fwiyc2makttvptrymxndp.png" alt="Image" width="416" height="112"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Workload
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media2.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%2F0xarwo1kuqowfy8urm7o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F0xarwo1kuqowfy8urm7o.png" alt="Image" width="800" height="106"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The numbers in green show how many tasks an engineer is working on during each week. These values are calculated automatically: the formula basically counts how many times each engineer initials appear within current week’s column. It can be 0 if an engineer is working on a different project or is on holidays. Ideally the workload should be 0.5 or 1. If you plan 2 full-week tasks for the same engineer, the cell turns red:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fxwkhrsagwtd6bc6pa7ge.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fxwkhrsagwtd6bc6pa7ge.png" alt="Image" width="615" height="314"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Progress
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fktem6ophchzrpajqseth.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fktem6ophchzrpajqseth.png" alt="Image" width="800" height="93"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For every week we can calculate project completion progress. We assume that all tasks in the past are complete, otherwise we’d extend or move move them to a time slot in the future. Thus, to estimate progress percentage we can just divide the number of eng weeks &lt;strong&gt;before&lt;/strong&gt; the current moment (to the left on the timeline) by &lt;strong&gt;total&lt;/strong&gt; eng weeks:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fju9qokfftpeu0ltl9e7z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fju9qokfftpeu0ltl9e7z.png" alt="Image" width="800" height="406"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is better than just counting number of complete tasks because the formula takes task size (in weeks) into consideration.&lt;/p&gt;

&lt;p&gt;In this section we also indicate holidays, milestones, release phases (QA, launch), team events (e.g. offsites).&lt;/p&gt;

&lt;h2&gt;
  
  
  How we used the tracker
&lt;/h2&gt;

&lt;p&gt;On Tuesdays we were having engineering syncs where we’d look at the timeline together and check if we’re following the plan. If a task took longer than planned, we would extend it by 1/2 week or 1 week on the timeline, and then check if the workload for the engineer hasn’t exceeded 1. If it was above 1, the we had to push other tasks or pass to a different engineer, or deprioritise a task, or communicate delay for the launch date.&lt;/p&gt;

&lt;p&gt;On Thursdays we were having syncs with a broader group of stakeholders. In those meetings we were sharing project status: whether we are on track or not, and project completion %.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pros and cons
&lt;/h3&gt;

&lt;p&gt;Using a custom tracker task tracker like this one comes with a few downsides compared to a specialised tool. But we believe the benefits are worth it. Let’s take a look at the pros and cons.&lt;/p&gt;

&lt;h4&gt;
  
  
  🔴 No integration with existing tools (Tasks, Diffs)
&lt;/h4&gt;

&lt;p&gt;A task is just a line in a spreadsheet, it doesn’t have a task number, it cannot be linked to a diff. For some projects it may be a problem. We normally use tasks tool, but typically a task only has a name and brief description. There’s no discussion in the comments, it usually happens offline. If you actually don’t use tasks for collaboration, our tracker might work for you as well.&lt;/p&gt;

&lt;h4&gt;
  
  
  🔴 Manually updating task status
&lt;/h4&gt;

&lt;p&gt;If you want to mark task as completed, you should color the cell. If you want to pass task to a new owner, or reschedule tasks, you have to edit the cells in the spreadsheet. There’s a possibility that you make a mistake. I made mistakes a few times, e.g. I moved eng weeks on the timeline to the wrong row. It turned out that these errors are easy to spot and fix once we got to task implementation. Spreadsheet editing history can be helpful.&lt;/p&gt;

&lt;h4&gt;
  
  
  🔴 Need to learn the conventions
&lt;/h4&gt;

&lt;p&gt;Such as how we indicate task status, or how to read completion %. In our team we had one PoC who was keeping the tracker updated (me). It wasn’t hard, and it took ~10mins a week during Eng syncs.&lt;/p&gt;

&lt;h4&gt;
  
  
  🟢 Everybody already knows how to use it
&lt;/h4&gt;

&lt;p&gt;Because everybody knows Excel. The tracker is just a google spreadsheet, it doesn’t use any scripts and the formulas are basic. Everybody already knows how to use it, how to color a cell and change the contents. It’s only a matter of learning the conventions.&lt;/p&gt;

&lt;h4&gt;
  
  
  🟢 Customisation possibilities
&lt;/h4&gt;

&lt;p&gt;You can tailor the tracker to your project needs. You want to add task priority? No problem, just add a column:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fybtjc0wroal4ptl8nhi9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fybtjc0wroal4ptl8nhi9.png" alt="Image" width="602" height="809"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We have actually added this column right in the middle of a meeting with stakeholders where we decided to prioritise a few features over others.&lt;/p&gt;

&lt;p&gt;It only takes a minute to add a field to your tasks. Custom columns work well with conditional formatting (spreadsheet can set cell color based on the contents, e.g. if value = ‘High’ then set color to red) and filtering.&lt;/p&gt;

&lt;p&gt;Or let’s say you want to hide completed tasks or weeks that passed already. No problem, just hide those rows or columns.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fr0yxgin1x4nmyhp73yv8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fr0yxgin1x4nmyhp73yv8.png" alt="Image" width="800" height="510"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You want to highlight risky tasks, or tasks with dependencies on other teams? No worries, spreadsheet’s color palette is at your disposal:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fpez8mzb97exncf8bq13a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fpez8mzb97exncf8bq13a.png" alt="Image" width="800" height="414"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  🟢 Productivity
&lt;/h4&gt;

&lt;p&gt;If you need to set a field for many tasks (e.g. PoC or priority), just use the magic corner. It’s fast and everybody knows how to do this. Same for timeline: if you need to extend your project by a few weeks, just extend calendar to the right.&lt;/p&gt;

&lt;h3&gt;
  
  
  Outcome
&lt;/h3&gt;

&lt;p&gt;The tracker helped us greatly, it was easy to track project status both within engineering team and with a wider group of XFNs. It also helped us take action early: when we realised we won't be able to complete the project with current scope before code freeze, so we had to re-prioritise a few parts.&lt;br&gt;
Please share your thoughts&lt;br&gt;
Do you think this can work for your project? Do you know a better way? What tracker are you using? &lt;strong&gt;Please share in the comments!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Link to tracker template:&lt;br&gt;
&lt;a href="https://docs.google.com/spreadsheets/d/1wvjITX06ceToX1dFNpx0iYasUEiQd4mqXJ3f_RerzDI/edit?usp=sharing" rel="noopener noreferrer"&gt;https://docs.google.com/spreadsheets/d/1wvjITX06ceToX1dFNpx0iYasUEiQd4mqXJ3f_RerzDI/edit?usp=sharing&lt;/a&gt;&lt;/p&gt;




&lt;h4&gt;
  
  
  P.S.: More automation with Apps Script
&lt;/h4&gt;

&lt;p&gt;In our tracker we had to update current week indication (red vertical line) manually. It can be automated, a script will be just a few lines. Here’s an example of a similar script which I used for my personal project, can be an inspiration: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2F3c0ggqemhnsxj6p9969m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F3c0ggqemhnsxj6p9969m.png" alt="Image" width="800" height="357"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Automating Google Spreadsheets with a script turned out to be straightforward, script are written in JavaScript (not Visual Basic fortunately). It opens possibilities for automating more flows.&lt;/p&gt;

</description>
      <category>emptystring</category>
    </item>
  </channel>
</rss>
