<?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: Dmitriy Kovalenko</title>
    <description>The latest articles on DEV Community by Dmitriy Kovalenko (@dmtrkovalenko).</description>
    <link>https://dev.to/dmtrkovalenko</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%2F201902%2F92e6e349-0557-4197-9137-bdf7f51173ba.jpeg</url>
      <title>DEV Community: Dmitriy Kovalenko</title>
      <link>https://dev.to/dmtrkovalenko</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dmtrkovalenko"/>
    <language>en</language>
    <item>
      <title>Benchmark oriented development is a road to nowhere</title>
      <dc:creator>Dmitriy Kovalenko</dc:creator>
      <pubDate>Wed, 25 Mar 2026 22:33:40 +0000</pubDate>
      <link>https://dev.to/dmtrkovalenko/benchmark-oriented-development-is-a-road-to-nowhere-1518</link>
      <guid>https://dev.to/dmtrkovalenko/benchmark-oriented-development-is-a-road-to-nowhere-1518</guid>
      <description>&lt;p&gt;Cursor just released this &lt;a href="https://cursor.com/blog/fast-regex-search" rel="noopener noreferrer"&gt;article&lt;/a&gt; and a ton of people started worshiping Cursor like they just made a revolution in file search. They showed a beautiful graph saying that they are 1,300x faster than ripgrep, showing one specific query on the chromium codebase. You know, I happen to work a lot lately on the file search project of mine &lt;a href="https://github.com/dmtrKovalenko/fff.nvim" rel="noopener noreferrer"&gt;https://github.com/dmtrKovalenko/fff.nvim&lt;/a&gt; and I have a feeling that this is all one large manipulation a lot of people blindly believed.&lt;/p&gt;

&lt;p&gt;We cannot say for sure because as always they do not open source the code and do not let you repeat the experiment, which makes this whole discussion absolutely useless, but I'll try to be constructive.&lt;/p&gt;

&lt;p&gt;Here are my claims&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The data is closed
&lt;/h2&gt;

&lt;p&gt;I like to say myself that ripgrep is not the fastest code search especially on macos, but I specifically asked my colleague to give me his M2 macbook air and I spent quite some time experimenting but I never got the ripgrep on the same repo with the same query longer executing longer than 9 seconds, while usuallyit takes around 6 seconds when the cache is warmed up.&lt;/p&gt;

&lt;p&gt;The main problem of all of those closed benchmarks and weird marketing articles that you can not verify anything yourself. You have neither the data nor the instrument to reproduce the success. You know, there is a reason why every research paper has to describe the experiment - so other people can reproduce it. &lt;/p&gt;

&lt;p&gt;But let's try to figure out what they did&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Indexing the code locally
&lt;/h2&gt;

&lt;p&gt;As they mentioned in their blog, indexing is a very old technique and trigram search was first mentioned 80 years ago. So how did Cursor come up with such a beautiful solution only in 2026? Is everyone around dumb and never did anything like this before? No, actually all the theory in the blog post they made (only the one that makes sense) comes from this paper &lt;a href="https://swtch.com/%7Ersc/regexp/regexp4.html" rel="noopener noreferrer"&gt;https://swtch.com/~rsc/regexp/regexp4.html&lt;/a&gt; that was behind the Google Code Search project.&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%2Fzguqmd2hug1cmwv183nd.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%2Fzguqmd2hug1cmwv183nd.png" alt="Google code search paper " width="800" height="951"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So why didn't any of the editors before come up with the solution of using indexes? This is my main question to the Cursor team. Why didn't you mention that indexing the same repo on the same machine takes &amp;gt;3 minutes. &lt;/p&gt;

&lt;p&gt;And it takes 1GB of space which tbh is not that much for 487,592 searchable non-binary files. Your implementation very likely takes MORE TIME but is likely more space efficient (maybe 2-4 times? we never know cause this is all just words)&lt;/p&gt;

&lt;p&gt;So dear Cursor marketers, why didn't you consider this chart to be added to your blog post? Yes I'm using the infinite super power of marketing bar charts against you:&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%2Fx63jtxorow30wolcqwf5.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%2Fx63jtxorow30wolcqwf5.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You know the biggest problem is not indexing the file, the biggest issue is maintaining the index, and this is the primary reason why every editor like VSCode is not using something like this. Inverted trigram indexes are actually a complicated thing. And you made it even more complicated. If we take ONLY the ASCII typeable characters subset you will have about 700k unique trigrams listed in the chromium codebase&lt;/p&gt;

&lt;h3&gt;
  
  
  A bit about the n-gram indexing
&lt;/h3&gt;

&lt;p&gt;Tri-gram or bi-gram is just a unique pairs or trios of bytes within a string. Either the document or the query.&lt;/p&gt;

&lt;p&gt;The trigram index is going to look like this in memory, there is a bunch of ways you can optimize it like using bitfields instead of arrays to reduce the space but the amount of data is massive.&lt;/p&gt;

&lt;p&gt;(the amount of rows would be equal to every unique 3 characters in your codebase)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────┬───────────┐
│ trigram │ file_idx  │
├─────────┼───────────┤
│ "  M"   │ [1, 2, 3] │
│ " MA"   │ [1, 2]    │
│ "MAX"   │ [1, 2]    │
│ "AX_"   │ [1, 2]    │
│ "X_F"   │ [1]       │
│ "_FI"   │ [1, 3]    │
│ "FIL"   │ [1, 3]    │
│ "ILE"   │ [1, 3]    │
│ "LE_"   │ [1, 3]    │
│ "E_S"   │ [1, 3]    │
│ "_SI"   │ [1, 3]    │
│ "SIZ"   │ [1, 3]    │
│ "IZE"   │ [1, 3]    │
│ "ZE "   │ [1, 3]    │
└─────────┴───────────┘

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

&lt;/div&gt;



&lt;p&gt;The problem is that to keep it fast it absolutely has to use a flat allocated array of all the files somewhere to keep the lookup table fast. This brings 2 problems&lt;/p&gt;

&lt;p&gt;a) you have to know and store the list of all the files in the file system&lt;br&gt;
b) you have to update both file list and index every time something has changed&lt;/p&gt;

&lt;p&gt;The blog post mentions that they use "layers" to keep the sync up-to-date. It is a smart move but the blog post never mentions ANYTHING about the way they do the layering. And this is actually the most important part because if they were able to figure out how to quickly and efficiently handle the "file deleted at index N" problem without hitting conflicts or growing another 1GB file next to the original index, this is actually something worth discussing. And they simply omitting it?! &lt;/p&gt;

&lt;p&gt;Where do they do the updates? Do they use a persistent file watcher? Where index is stored, how much time do they spend on traversing the tree? What is going to happen when a user wants to search in &lt;code&gt;node_modules&lt;/code&gt;? We don't know because they specifically omit this part entirely.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;we control the state of the index by basing it off a commit in the underlying Git repository. User and agent changes are stored as a layer on top of it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So you are saying that every single commit a user makes, you will spend 3 minutes indexing? Great job, but in fact they just do the indexing in the background, effectively falling back to regex search in the meantime. Because reindexing is a non-atomic operation that requires a lock.&lt;/p&gt;

&lt;p&gt;This is the essential problem of indexing that IS NOT solved. You do the marketing like you solved this problem or it is not imporant while in fact this is exactly what is important. &lt;/p&gt;

&lt;p&gt;My project uses in-memory indexing that looks pretty similar to what they describe and I'll tell you later how we bypass this exact limitation in fff.&lt;/p&gt;
&lt;h2&gt;
  
  
  3. n-gram indexing and its cost
&lt;/h2&gt;

&lt;p&gt;So I actually read the blog post a few times because I am genuinely interested in how they do this and this is the only paragraph that actually describes something. Everything else is pure water. But the funniest part of this paragraph is that to me it looks like a complete LLM hallucination. In short: they are building an inverted index of the trigrams but adding a bloom filter to the next byte.&lt;/p&gt;

&lt;p&gt;The way n-gram inverted index works is very simple &lt;/p&gt;

&lt;p&gt;Here is the first suspicious part making me think they didn't really understand the point of trigrams&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;By having a mask that contains the characters following each trigram, our inverted index can be constructed using trigram keys, but we can query it using quadgrams! This already scopes down the potential documents much more than a simple trigram index could.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So I actually ran the n-gram indexing for the chromium project. And here is what I found for the exact "MAX_FILE_SIZE" query over the same indexed code using trigram and bi-gram search.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────┬───────────────────┬────────────────────┬───────┬───────┐
│      Query      │ Bigram candidates │ Trigram candidates │ Bi %  │ Tri % │
├─────────────────┼───────────────────┼────────────────────┼───────┼───────┤
│ MAX_FILE_SIZE   │ 13,241            │ 727                │ 3.0%  │ 0.2%  │
├─────────────────┼───────────────────┼────────────────────┼───────┼───────┤
│ mutex_lock      │ 13,934            │ 279                │ 3.2%  │ 0.1%  │
├─────────────────┼───────────────────┼────────────────────┼───────┼───────┤
│ return          │ 179,617           │ 140,367            │ 40.8% │ 31.9% │
├─────────────────┼───────────────────┼────────────────────┼───────┼───────┤
│ #include        │ 127,705           │ 125,227            │ 29.0% │ 28.5% │
├─────────────────┼───────────────────┼────────────────────┼───────┼───────┤
│ TODO            │ 191,186           │ 32,865             │ 43.4% │ 7.5%  │
├─────────────────┼───────────────────┼────────────────────┼───────┼───────┤
│ struct file     │ 92,419            │ 22,337             │ 21.0% │ 5.1%  │
├─────────────────┼───────────────────┼────────────────────┼───────┼───────┤
│ phylink_ethtool │ 3,069             │ 2                  │ 0.7%  │ 0.0%  │
└─────────────────┴───────────────────┴────────────────────┴───────┴───────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is absolutely no reason to read the article further because for the exact query they showed, the trigram index actually returns &amp;lt;1k candidates, and there is absolutely no reason to optimize it even further. Unless of course you run your LLM in a loop asking to come up with a better approach (LLMs love to put bloom filters everywhere, just saying)&lt;/p&gt;

&lt;p&gt;For any normal sized repository (e.g. 20k lines) quadgram indexing would essentially be equal to the exact search (every query will result in 1-2 candidates). This is simply an overkill even for a chromium 500k files repo.&lt;/p&gt;

&lt;p&gt;But on top of adding an additional bloom filter imitating quadgrams, they decided to prefilter by frequency and hash every single trigram (there are 779,889 distinct trigrams in chromium), effectively making indexing way slower simply to reduce 727 candidates to 300. There are a ton of questions about how this works that we will never get answers to because this is a private proprietary thing. I have no idea why we are even discussing this.&lt;/p&gt;

&lt;p&gt;In addition to that, with their idea of dynamic n-grams + a bloom filter, they now have the problem of storing the data because there is no primitive that can fit those as a key in the address space (and no, this is not because they support full Unicode indexing), so they have to hash every single key to simply store the index on the file system.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Ultimate performance problem
&lt;/h2&gt;

&lt;p&gt;I once said that FFF is the fastest file search on the planet. While it was true, I never thought that this was the killer feature. Once in a while I get tweets like "fff feels slow in the 200GB Android root repo". I usually ignore those because I actually work with a former Android core dev now and I asked them how they do Android development and they told me that NO ONE EVER opens the Android root. The same thing happens with chromium, it literally mixes Android, iOS, web, V8, and other subprojects.&lt;/p&gt;

&lt;p&gt;The indexing itself is a complicated operation which doesn't actually give any performance benefit on &lt;em&gt;real&lt;/em&gt; repositories but anyway takes time to build and store. Without index fff is able to search the Linux kernel for a very rare string in under 6ms (the average repo you're gonna work with is way less than 100k files) which is absolutely fine, and under 1ms with the very simple index we have:&lt;/p&gt;

&lt;h3&gt;
  
  
  Index optimized for the actual users
&lt;/h3&gt;

&lt;p&gt;for large repositories FFF is actually doing a very similar approach to what is described, and I originally thought they just stole my code (seems like not really 😭). We use the inverted bigram index approach but only over the most useful and rare ones. There is absolutely 0 reason to even index bigrams like "th" because the word "the" will exist in the absolute majority of files.&lt;/p&gt;

&lt;p&gt;This is not what I even call a feature, but an optimization because n-gram indexing never works on the real Unicode files (because once you have many languages you will need to pick up the actual trigrams that exist in the words rather than all the combinations, and this simply doesn't work with source code files). &lt;/p&gt;

&lt;p&gt;Here is how fff in-memory bi-gram prefiltering works:&lt;/p&gt;

&lt;p&gt;If we take the 95 printable ASCII characters and lowercase them we get 69 distinct values (a-z, 0-9, punctuation, space). 69² = 4,761 possible unique bigram pairs - that's it. No banning, no frequency cutoffs. We index ALL of them because the key space is naturally bounded and tiny compared to trigrams (69³ = 328,509 keys). During the indexing phase we do the same reading of all the files to:&lt;/p&gt;

&lt;p&gt;a) populate the virtual memory mapped cache&lt;br&gt;
b) read the file content and extract bigrams for the inverted index&lt;/p&gt;

&lt;p&gt;Because bigram extraction is just splitting bytes by 2 and lowercasing, modern CPUs chew through it because it is easy SIMD problem. Indexing the whole chromium repository takes around 2 seconds to list files and 7 seconds for the bigram index which runs completely asynchronously:&lt;/p&gt;

&lt;p&gt;2026-03-24T19:43:11.331524Z  INFO fff_search::file_picker: crates/fff-core/src/file_picker.rs:1115 Bigram index built in 7.59s - 4761 columns (3481 sparse, 1280 dense) for 487592 files&lt;/p&gt;

&lt;p&gt;This is faster than gathering git status for those files using libgit2! The prefilter index is stored as a compressed immutable inverted index with a hybrid column storage - each bigram gets either a dense bitset or a sparse posting list, whichever is smaller.&lt;/p&gt;

&lt;p&gt;Here's the full layout:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;LOOKUP TABLE (178 KB fixed)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Maps every possible bigram key (u16) to its column index.
44,587 entries × 4 bytes. Most map to NO_COLUMN (bigram never seen).

┌──────────┬──────────┬──────────┬──────────┬───┬──────────┐
│ key 0    │ key 1    │ key 2    │ key 3    │...│ key 65535│
│ col: ──  │ col: ──  │ col: 0   │ col: ──  │   │ col: ──  │
└──────────┴──────────┴────┬─────┴──────────┴───┴──────────┘
                           │
                           ▼
COLUMNS (4,761 for chromium — one per unique lowercased ASCII bigram)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Each column stores "which files contain this bigram" in one of two formats:

BITSET (1,280 columns — common bigrams like "ma", "in", "re")
┌──────────────────────────────────────────────────────────────────────┐
│ One bit per file. For 487k files → 7,610 u64 words → ~60 KB/col      │
│                                                                      │
│ Column for bigram "fi":                                              │
│ ┌──────────────────┬──────────────────┬──────────────────┬─────┐     │
│ │ word 0           │ word 1           │ word 2           │ ... │     │
│ │ files 0–63       │ files 64–127     │ files 128–191    │     │     │
│ │ 0b..00101001     │ 0b..10000100     │ 0b..00000011     │     │     │
│ │    ↑  ↑  ↑       │    ↑     ↑       │ ↑ ↑              │     │     │
│ │    0  3  5       │   66    71       │128 129           │     │     │
│ └──────────────────┴──────────────────┴──────────────────┴─────┘     │
│ Query: result &amp;amp;= column  (word-by-word AND, SIMD-friendly)           │
└──────────────────────────────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And this is how the filtering process looks like. I do not have time and resources to build and interactive visualization but I think this is actually more clear:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;QUERY: grep "MAX_FILE_SIZE"
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1. Extract bigrams: "ma","ax","x_","_f","fi","il","le","e_","_s","si","iz","ze"
2. Look up each in the table → get column indices
3. AND all columns together:

   result = 1111...1111  (start: all files are candidates)
   result &amp;amp;= col["ma"]  →  ████░░░░████░░██░░░░...  (files with "ma")
   result &amp;amp;= col["ax"]  →  ██░░░░░░░██░░░██░░░░...  (intersect with "ax")
   result &amp;amp;= col["x_"]  →  █░░░░░░░░░█░░░█░░░░░...  (getting smaller)
   ...                      ↓ after all 12 bigrams:
   result = 13,241 bits set out of 487,592  →  97% of files eliminated

OVERLAY (live updates without reindexing)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
The base index is immutable. File changes go to a delta layer:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The crossover point between sparse and dense is elegant: when &lt;code&gt;popcount × 4 bytes ≥ ⌈file_count/64⌉ × 8 bytes&lt;/code&gt;, the column switches from a posting list to a bitset. This happens at ~3.1% file occupancy. For chromium that means a bigram appearing in 15,000+ files gets a dense bitset, while rarer bigrams use compact posting lists. We store all 4,761 distinct case-insensitive ASCII bigrams - not losing anything on the prefiltering step.&lt;/p&gt;

&lt;p&gt;Because fff is originally built with a background fs watcher in mind and an immutable file tree index, this is extremely easy for us to update the index every time a file is updated or deleted, though it might take ~10 seconds if the whole scan is invalidated. Here are real measurements on actual codebases (Apple M4 Max, all times measured from scan complete to index ready):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────┬─────────┬────────────┬──────────────────────┬──────────┬──────────┐
│ Repository      │  Files  │  Indexing  │ Cols (sparse/dense)  │  Memory  │  B/file  │
├─────────────────┼─────────┼────────────┼──────────────────────┼──────────┼──────────┤
│ whisper.cpp     │   1,150 │    0.10s   │ 4,320 (2,534/1,786)  │   747 KB │    665   │
│ ffmpeg          │   9,834 │    0.31s   │ 4,672 (3,078/1,594)  │   2.9 MB │    309   │
│ rust-skia       │  12,502 │    0.41s   │ 4,542 (3,081/1,461)  │   3.5 MB │    294   │
│ &amp;lt;Your B2B SAAS&amp;gt; │  21,462 │    0.52s   │ 4,314 (2,823/1,491)  │   3.7 MB │    269   │
│ linux kernel    │  98,462 │    1.94s   │ 4,489 (3,202/1,287)  │  23.7 MB │    287   │
│ chromium        │ 487,592 │    7.87s   │ 4,761 (3,481/1,280)  │ 113.1 MB │    243   │
└─────────────────┴─────────┴────────────┴──────────────────────┴──────────┴──────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Okay and here is the real comparison, where you can literally see that the value coming from persistent indexing is visible only on the VERY LARGE repositories.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌───────────────────────┬──────────────────┬──────────────────────┬──────────────────────┬──────────────────────┐
│ Repository            │ Tool             │   Time to Index      │   Index Size         │   Time to Query      │
├───────────────────────┼──────────────────┼──────────────────────┼──────────────────────┼──────────────────────┤
│ whisper.cpp (1,150)   │ Cursor*          │   ~14 s*             │   ~2 MB*             │   ?                  │
│                       │ zoekt            │   10s                │   4MB                │   ~3 ms              │
│                       │ fff              │   0.10 s (async)     │   747 KB             │   ~1 ms              │
│                       │ ripgrep          │   - (no index)       │   - (no index)       │   ~15 ms             │
├───────────────────────┼──────────────────┼──────────────────────┼──────────────────────┼──────────────────────┤
│ ffmpeg (9,834)        │ Cursor*          │   ~45 s*             │   ~20 MB*            │   ?                  │
│                       │ zoekt            │   28s                │   43MB               │   ~1 ms              │
│                       │ fff              │   0.31 s (async)     │   2.9 MB             │   ~1 ms              │
│                       │ ripgrep          │   - (no index)       │   - (no index)       │   ~120 ms            │
├───────────────────────┼──────────────────┼──────────────────────┼──────────────────────┼──────────────────────┤
│ chromium (487,592)    │ Cursor*          │   ~4 min*            │   ~1 GB*             │   ?                  │
│                       │ zoekt            │   2 min              │   1.4GB              │   ~3 ms              │
│                       │ fff              │   7.87 s (async)     │   113.1 MB           │   118 ms             │
│                       │ ripgrep          │   - (no index)       │   - (no index)       │   ~6 s               │
└───────────────────────┴──────────────────┴──────────────────────┴──────────────────────┴──────────────────────┘
* Cursor values are estimates - code is not open source, measurements cannot be reproduced
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The question is: do you want to wait 3 minutes and burn 1GB of disk for a 35x improvement on the one repo nobody opens as a whole? Or do you want instant results on every real repository you actually work on?&lt;/p&gt;

&lt;p&gt;Yes fff is 10 times slower than the "ephemeral code they showed" while effectively having no persistent index stored, and is still 34x faster than ripgrep (because ripgrep didn't take 16 seconds to find the files, it takes 6, let's be honest) but on the real repositories every query runs in sub-ms &lt;/p&gt;

&lt;p&gt;and here is why even this doesn't really matter&lt;/p&gt;

&lt;h2&gt;
  
  
  5. I heard you are an AI agentic company, right?
&lt;/h2&gt;

&lt;p&gt;All I want to say is that the main problem of file search is not the speed, it's accuracy. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It's like typing speed, I can type 200 words per minute with a 99% typo rate. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The main problem of file search that I am trying to solve is the way we suggest files for the user / AI agent. Yes I recently released fff-mcp that is replacing built-in file commands and actually delivers +10% of speed to your clanker run. Though it's not because of the search itself, it's because of the reduced round trips of the agent.&lt;/p&gt;

&lt;p&gt;Let's try to do a search on chromium for actual code. You can literally see that LLMs LOVE to stack all kinds of guards to reduce the amount of incoming files on their own, similar to what is happening to the&lt;/p&gt;

&lt;p&gt;Here is a beautiful example of 2 sessions, one with Claude's default tools and the one with fff&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%2Fhg4yd83zgcwmagbh4o52.gif" 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%2Fhg4yd83zgcwmagbh4o52.gif" alt="Claude code search comparison" width="8" height="4"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The one with fff mcp that is 3x less time and 4x less cost (lmao I should've used this in the announcement)&lt;/p&gt;

&lt;p&gt;Most of the queries immediately look like "v8/ " which is parsed by the fff query parsing and does very fast SIMD-optimized prefiltering in the paths 4ns + bigram index 8ns, having the file search only touch 458 files on the real query generated by the agent. This query returned results in under 2ms&lt;/p&gt;

&lt;p&gt;And the biggest value is NOT THE RAW SPEED of a file search, it's the reduced amount of round trips because fff has built-in fuzzy code matching that is pretty slow (it's impossible to prefilter) but it does show suggestions for your LLM, significantly reducing round trips. This is where the actual optimization for the coding agents is, and you folks are missing that.&lt;/p&gt;

&lt;p&gt;In addition to that, fff knows about git status of every file, how often it's modified, it classifies which lines are definitions (function names, classes, variables) to suggest them first, keeps track of frecency and sorts the files by all kinds of heuristics (which is very slow) to prevent the user, whether a human or AI agent, from repeating the search which is exactly where the time waste is.&lt;/p&gt;

&lt;p&gt;And it can still finish the prefiltering index on a 500k file codebase faster than an LLM will generate the first tool call.&lt;/p&gt;

&lt;p&gt;Cheers.&lt;/p&gt;

</description>
      <category>cursor</category>
      <category>ai</category>
      <category>programming</category>
    </item>
    <item>
      <title>How act() works inside react?</title>
      <dc:creator>Dmitriy Kovalenko</dc:creator>
      <pubDate>Wed, 17 Feb 2021 22:30:49 +0000</pubDate>
      <link>https://dev.to/dmtrkovalenko/how-act-works-inside-react-3hc0</link>
      <guid>https://dev.to/dmtrkovalenko/how-act-works-inside-react-3hc0</guid>
      <description>&lt;p&gt;Hola! Lazy dev here. &lt;/p&gt;

&lt;p&gt;React testing is hard. Especially react testing outside the browser environment, like with Jest and JSdom. Let's try to reverse engineer react's &lt;code&gt;act()&lt;/code&gt;, understand why do we need it, and think about UI testing overall.&lt;/p&gt;

&lt;h2&gt;
  
  
  History
&lt;/h2&gt;

&lt;p&gt;Today I meet this tweet by @floydophone &lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1362123964412096513-153" src="https://platform.twitter.com/embed/Tweet.html?id=1362123964412096513"&gt;
&lt;/iframe&gt;

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



&lt;/p&gt;

&lt;p&gt;And was inspired to write about how your tests work inside your terminal when you are testing in node.js. Let's start from the question – why do we need this "magic" &lt;code&gt;act()&lt;/code&gt; function. &lt;/p&gt;

&lt;h2&gt;
  
  
  What is act()
&lt;/h2&gt;

&lt;p&gt;Here is a quote from &lt;a href="https://reactjs.org/docs/test-utils.html#act" rel="noopener noreferrer"&gt;react.js docs&lt;/a&gt;: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;To prepare a component for assertions, wrap the code rendering it and performing updates inside an act() call. This makes your test run closer to how React works in the browser.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So the problem that &lt;code&gt;act()&lt;/code&gt; is solving – &lt;strong&gt;It delays your tests until all of your updates were applied&lt;/strong&gt; before proceeding to the next steps. When you are doing any kind of user interaction, like this &lt;/p&gt;

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

&lt;span class="nf"&gt;act&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;button&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dispatchEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MouseEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&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="na"&gt;bubbles&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="p"&gt;});&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;React is not updating UI immediately, thanks to the Fiber architecture. It will update it asynchronously in some time after the click, so we need to wait for UI to be updated. &lt;/p&gt;

&lt;h2&gt;
  
  
  And here is a problem
&lt;/h2&gt;

&lt;p&gt;The main problem here – &lt;code&gt;act()&lt;/code&gt; is actually a crutch and you will probably agree that it is not a perfect solution. Tests that you (probably) are writing are synchronous. It means that commands and assertions that tests are doing are executed one-by-one without any waiting. &lt;/p&gt;

&lt;p&gt;UI works differently – UI is async by nature. &lt;/p&gt;

&lt;h2&gt;
  
  
  Reverse engineer it
&lt;/h2&gt;

&lt;p&gt;Let's look more closely at the implementation of this function, right from the react sources. We only need 2 files &lt;a href="https://github.com/facebook/react/blob/b93f3e7d2d595444c6ee2964d4cce2e17f4ffa28/packages/react-dom/src/test-utils/ReactTestUtilsPublicAct.js" rel="noopener noreferrer"&gt;ReactTestUtilsPublicAct&lt;/a&gt; and &lt;a href="https://github.com/facebook/react/blob/e2fd460cca3b11fa0f5505f324043a9b2d9db894/packages/react-reconciler/src/ReactFiberWorkLoop.new.js" rel="noopener noreferrer"&gt;ReactFiberWorkLoop&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;I will skip not interesting parts, but the code is not so big so you can read it yourself 🙃 Let's start from the main point of the act function: &lt;/p&gt;

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

  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;result&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="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;batchedUpdates&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;callback&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="c1"&gt;// on sync errors, we still want to 'cleanup' and decrement actingUpdatesScopeDepth&lt;/span&gt;
    &lt;span class="nf"&gt;onDone&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;throw&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And this magic &lt;code&gt;batchedUpdates&lt;/code&gt; function has a pretty simple yet powerful implementation. &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;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;batchedUpdates&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;A&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;R&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;fn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;A&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;R&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;A&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;R&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;prevExecutionContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;executionContext&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;executionContext&lt;/span&gt; &lt;span class="o"&gt;|=&lt;/span&gt; &lt;span class="nx"&gt;BatchedContext&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="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;executionContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;prevExecutionContext&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;executionContext&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;NoContext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Flush the immediate callbacks that were scheduled during this batch&lt;/span&gt;
      &lt;span class="nf"&gt;resetRenderTimer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nf"&gt;flushSyncCallbackQueue&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="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;This particular function is called inside the react when during the render phase react exactly knows that all updates are done and we can render the dom. And it starts the reconciliation and synchronous dom updating after.&lt;/p&gt;

&lt;p&gt;After &lt;code&gt;batchedUpdates&lt;/code&gt; our code has 2 branches depends on how you used it. If you passed a synchronous function inside the &lt;code&gt;act&lt;/code&gt;, like&lt;/p&gt;

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

 &lt;span class="nf"&gt;act&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;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;(&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Counter&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;,&lt;/span&gt; &lt;span class="nx"&gt;container&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;It will call the function &lt;code&gt;flushWork&lt;/code&gt; which is nothing more than a sync &lt;code&gt;while&lt;/code&gt; loop  &lt;/p&gt;

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

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;flushWork&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="nx"&gt;Scheduler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;unstable_flushAllWithoutAsserting&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="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;didFlushWork&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;flushPassiveEffects&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;didFlushWork&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&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;didFlushWork&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;blockquote&gt;
&lt;p&gt;It looks like for concurrent mode a new specific hook implemented to stop all the effects together (&lt;code&gt;unstable_flushAllWithoutAsserting&lt;/code&gt;)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But for now, it is just a sync while loop that stops the synchronous execution until all the DOM updating work is done. Pretty clumsy solution, don't you think? &lt;/p&gt;

&lt;h2&gt;
  
  
  Async execution
&lt;/h2&gt;

&lt;p&gt;More interesting is coming when you are passing an async function as a callback. Lets go to another code branch: &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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
  &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
  &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;function&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// ... not interesting&lt;/span&gt;

&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;actingUpdatesScopeDepth&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isSchedulerMocked&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
        &lt;span class="nx"&gt;previousIsSomeRendererActing&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;true&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="nf"&gt;onDone&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nf"&gt;resolve&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="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// we're about to exit the act() scope,&lt;/span&gt;
    &lt;span class="c1"&gt;// now's the time to flush tasks/effects&lt;/span&gt;
    &lt;span class="nf"&gt;flushWorkAndMicroTasks&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="na"&gt;err&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt;&lt;span class="nb"&gt;Error&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;onDone&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;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&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="nf"&gt;resolve&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="p"&gt;},&lt;/span&gt;
  &lt;span class="nx"&gt;err&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;onDone&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Here we are waiting for our passed callback (the &lt;code&gt;result&lt;/code&gt; is returned by &lt;code&gt;batchedUpdates&lt;/code&gt; function) and if after we are going to more interesting function &lt;code&gt;flushWorkAndMicroTasks&lt;/code&gt;. Probably the most interesting function here :) &lt;/p&gt;

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


&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;flushWorkAndMicroTasks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;onDone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt;&lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&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="nf"&gt;flushWork&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;enqueueTask&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="nf"&gt;flushWork&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;flushWorkAndMicroTasks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;onDone&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="nf"&gt;onDone&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="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;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;onDone&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;It is doing the same as the sync version (that only calling &lt;code&gt;flushWork()&lt;/code&gt;). But it wraps the call &lt;code&gt;enqueueTask&lt;/code&gt;, which is a hack only to avoid &lt;code&gt;setTimeout(fn, 0)&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;
  an enqueueTask function
  &lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;enqueueTask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&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="k"&gt;void&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="nx"&gt;enqueueTaskImpl&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;null&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="c1"&gt;// read require off the module object to get around the bundlers.&lt;/span&gt;
      &lt;span class="c1"&gt;// we don't want them to detect a require and bundle a Node polyfill.&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;requireString&lt;/span&gt; &lt;span class="o"&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;require&lt;/span&gt;&lt;span class="dl"&gt;'&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;random&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;slice&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="mi"&gt;7&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;nodeRequire&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;module&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="kr"&gt;module&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;requireString&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
      &lt;span class="c1"&gt;// assuming we're in node, let's try to get node's&lt;/span&gt;
      &lt;span class="c1"&gt;// version of setImmediate, bypassing fake timers if any.&lt;/span&gt;
      &lt;span class="nx"&gt;enqueueTaskImpl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;nodeRequire&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kr"&gt;module&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;timers&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;setImmediate&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;_err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// we're in a browser&lt;/span&gt;
      &lt;span class="c1"&gt;// we can't use regular timers because they may still be faked&lt;/span&gt;
      &lt;span class="c1"&gt;// so we try MessageChannel+postMessage instead&lt;/span&gt;
      &lt;span class="nx"&gt;enqueueTaskImpl&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="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&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="k"&gt;void&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="nx"&gt;__DEV__&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="nx"&gt;didWarnAboutMessageChannel&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;didWarnAboutMessageChannel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&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;typeof&lt;/span&gt; &lt;span class="nx"&gt;MessageChannel&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;undefined&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;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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;This browser does not have a MessageChannel implementation, &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
                  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;so enqueuing tasks via await act(async () =&amp;gt; ...) will fail. &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
                  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Please file an issue at https://github.com/facebook/react/issues &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
                  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;if you encounter this warning.&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="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;channel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MessageChannel&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nx"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;port1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onmessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;port2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;undefined&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="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;enqueueTaskImpl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;task&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;p&gt;The main goal of this function is &lt;strong&gt;only&lt;/strong&gt; to execute a callback in the next tick of the event loop. That's probably why react is not the best in terms of bundle size :) &lt;/p&gt;

&lt;h2&gt;
  
  
  Why async?
&lt;/h2&gt;

&lt;p&gt;It is a pretty new feature, probably needed more for concurrent mode, but it allows you to immediately run stuff like &lt;code&gt;Promise.resolve&lt;/code&gt; aka microtasks for example when mocking API and changing real promise using &lt;code&gt;Promise.resolve&lt;/code&gt; with fake data. &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;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;ReactDOM&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-dom&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;act&lt;/span&gt; &lt;span class="p"&gt;}&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-dom/test-utils&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;AsyncApp&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;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setData&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;idle value&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;simulatedFetch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &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;fetchedValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fetched value&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;setData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fetchedValue&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&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;useEffect&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;simulatedFetch&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="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;


&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nf"&gt;beforeEach&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;// setup a DOM element as a render target&lt;/span&gt;
  &lt;span class="nx"&gt;el&lt;/span&gt; &lt;span class="o"&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;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;div&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// container *must* be attached to document so events work correctly.&lt;/span&gt;
  &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;should render with the correct text with sync act&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &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;act&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;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;(&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;AsyncApp&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;,&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toContain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;idle value&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;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;should render with the correct text with async act&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &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;await&lt;/span&gt; &lt;span class="nf"&gt;act&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &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;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;(&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;AsyncApp&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;,&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toContain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fetched value&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;Both tests passing 😌. Here is a live example (you can open sandbox and run tests inside using "Tests" tab): &lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://codesandbox.io/embed/eloquent-bhaskara-vl9pw?module=/src/App.test.js"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;It is fun that it works, but if you will change &lt;code&gt;Promise.resolve&lt;/code&gt; to literally anything like this: &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;fetchedValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rej&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;res&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fetched value&lt;/span&gt;&lt;span class="dl"&gt;"&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="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// it doesn't work ¯\_(ツ)_/¯&lt;/span&gt;


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

&lt;/div&gt;
&lt;h2&gt;
  
  
  Replace
&lt;/h2&gt;

&lt;p&gt;It is pretty easy to replace any &lt;code&gt;act()&lt;/code&gt; call by using simple &lt;code&gt;setTimeout(fn, 0)&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="nx"&gt;button&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dispatchEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MouseEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&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="na"&gt;bubbles&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="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rej&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="nx"&gt;res&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="p"&gt;});&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;will work in most cases :) &lt;a href="https://codesandbox.io/s/cool-buck-2mqez?file=/src/Counter.test.js" rel="noopener noreferrer"&gt;some sources&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  But why
&lt;/h2&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%2F9w63kf1jav2j7gzpm2kj.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%2F9w63kf1jav2j7gzpm2kj.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The main question – why do we need it? So much ~not good~ code that confuses everybody? The answer – our tests that are running inside node.js and trying to be "sync" while the UI as &lt;strong&gt;async&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;And that's why you will never need any kind of &lt;code&gt;act()&lt;/code&gt; if you are rendering React components in the real browser and using async test-runner, like &lt;a href="https://docs.cypress.io/guides/component-testing/introduction.html#What-is-Cypress-Component-Testing" rel="noopener noreferrer"&gt;Cypress for component testing&lt;/a&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  Thank you
&lt;/h2&gt;

&lt;p&gt;Thank you for reading, hope it is more clear why do we need &lt;code&gt;act()&lt;/code&gt; for most plain react unit testing. &lt;/p&gt;

&lt;p&gt;And no &lt;code&gt;act()&lt;/code&gt; was not harmed in the making of this article :D &lt;/p&gt;

</description>
      <category>react</category>
      <category>testing</category>
      <category>javascript</category>
      <category>inside</category>
    </item>
    <item>
      <title>Why our visual regression is so slow?</title>
      <dc:creator>Dmitriy Kovalenko</dc:creator>
      <pubDate>Fri, 13 Nov 2020 21:11:52 +0000</pubDate>
      <link>https://dev.to/dmtrkovalenko/why-our-visual-regression-is-so-slow-33dn</link>
      <guid>https://dev.to/dmtrkovalenko/why-our-visual-regression-is-so-slow-33dn</guid>
      <description>&lt;p&gt;Hola! Lazy dev here, and today we are going to discuss computer eyes 👁👁 that help us to test how our products look like. &lt;/p&gt;

&lt;p&gt;The visual regression tools that we are using every day are extremely slow. That is the fact. At least because the task to compare 2 images is hard. And I tried to fix this 👀. &lt;/p&gt;

&lt;p&gt;But first of all, let's take a look at how visual regression tools work under the hood and what they are actually doing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Image comparison
&lt;/h2&gt;

&lt;p&gt;Image comparison itself is pretty hard. We can not just compare 2 image buffers using "===" and even we can not take all the pixels from the image and compare them one by one. Why? Because human eyes can not see the difference between all the colors. For example, how do you think – do these colors are the same or not? &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%2F3nqsovqqbh12bsetkv3j.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%2F3nqsovqqbh12bsetkv3j.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If yes – 👍 you have a really good vision! But 95% of people will not spot the difference between &lt;code&gt;rgba(200, 100, 100, 250)&lt;/code&gt; and &lt;code&gt;rgba(200, 100, 95, 250)&lt;/code&gt; colors.&lt;/p&gt;

&lt;p&gt;That's why in order to &lt;strong&gt;visually&lt;/strong&gt; compare images – we need to take all the pixels in the image and it is a lot (for a full-HD screenshot (1920x1080) we have &lt;code&gt;1920 * 1080 = 2 090 880&lt;/code&gt; pixels) – iterate them one by one and calculate the &lt;a href="https://en.wikipedia.org/wiki/Color_difference" rel="noopener noreferrer"&gt;color difference&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;This is a hard task for computers. So the algorithm is: &lt;/p&gt;

&lt;p&gt;1) Read and decode the image&lt;br&gt;
2) Make a loop with &lt;strong&gt;millions&lt;/strong&gt; iterations, make some calculations and save the different pixels&lt;br&gt;
3) Make an image of different pixels &lt;br&gt;
4) Encode and save the image diff &lt;/p&gt;

&lt;p&gt;But the result is extremely helpful when you are testing the user interface:&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%2Fkyf7s0ygy38980nfbpfd.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%2Fkyf7s0ygy38980nfbpfd.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  The slowness
&lt;/h2&gt;

&lt;p&gt;But unfortunately, tools that we are commonly using to compare screenshots are extremely slow. And they are slow not because they had a bad code inside. The main problem is that they are often &lt;em&gt;written in the wrong language&lt;/em&gt;* or doing some &lt;strong&gt;useless job&lt;/strong&gt; under the hood.&lt;/p&gt;

&lt;p&gt;For example, the most popular image comparison tool in the javascript community – &lt;a href="https://github.com/mapbox/pixelmatch" rel="noopener noreferrer"&gt;pixelmatch&lt;/a&gt; is really slow in Node.js environment (but blazing fast in the browser otherwise). &lt;/p&gt;

&lt;p&gt;Using pixelmatch to comparing 2 screenshots of &lt;a href="https://cypress.io" rel="noopener noreferrer"&gt;https://cypress.io&lt;/a&gt; home page will take around 7-8 seconds. &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%2Fbnnt2j1j7fjgn0e82t10.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%2Fbnnt2j1j7fjgn0e82t10.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Impact on your CI
&lt;/h2&gt;

&lt;p&gt;This really affects our CI time. Let's calculate the CI time for visual regression if we are running 25000 screenshot tests per month. And this number is not something overwhelming. It is a very basic plan of &lt;a href="https://percy.io" rel="noopener noreferrer"&gt;https://percy.io&lt;/a&gt;, which is usually &lt;strong&gt;not enough&lt;/strong&gt; for huge projects. &lt;/p&gt;

&lt;p&gt;So, if we are running 25000 visual tests and each screenshot test running for 7 seconds we are spending &lt;strong&gt;48,6 hours&lt;/strong&gt; on CI!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;25000 * 7 / 3600 = 48,611111111
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;That is a lot! This can take even more time than all the other UI tests and that's why performance for this sort of task &lt;strong&gt;really matters&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Cause if we can save at least 3 seconds per each snapshot we will save &lt;strong&gt;20 hours&lt;/strong&gt; per month.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;25000 * 3 / 3600 = 20,83333 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Solution
&lt;/h2&gt;

&lt;p&gt;That was a pain point for me, so I decided to fix this – and wrote that &lt;strong&gt;fastest in the world image comparison tool&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;And I am happy to introduce you &lt;a href="https://github.com/dmtrKovalenko/odiff" rel="noopener noreferrer"&gt;odiff&lt;/a&gt;! 👀🥳🎉 It was designed to handle the "huge" images, be fast, memory-efficient, and save &lt;strong&gt;your CI time&lt;/strong&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%2Fi%2Fmc5lilp2ig60b5njwtxq.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%2Fmc5lilp2ig60b5njwtxq.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This tool is running the same comparison 2 times faster than analogs! Yes, it can save you those 3 seconds per snapshot :) &lt;/p&gt;

&lt;p&gt;Here are some benchmarks: &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%2F0lbdghivm2tz0ccjw702.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%2F0lbdghivm2tz0ccjw702.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Also, here are the results of comparing the same cypress.io home page screenshot: &lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Mean [s]&lt;/th&gt;
&lt;th&gt;Min [s]&lt;/th&gt;
&lt;th&gt;Max [s]&lt;/th&gt;
&lt;th&gt;Relative&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pixelmatch www.cypress.io-1.png www.cypress.io.png www.cypress-diff.png&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;7.712 ± 0.069&lt;/td&gt;
&lt;td&gt;7.664&lt;/td&gt;
&lt;td&gt;7.896&lt;/td&gt;
&lt;td&gt;1.82 ± 0.03&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ImageMagick &lt;code&gt;compare www.cypress.io-1.png www.cypress.io.png -compose src diff-magick.png&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;8.881 ± 0.121&lt;/td&gt;
&lt;td&gt;8.692&lt;/td&gt;
&lt;td&gt;9.066&lt;/td&gt;
&lt;td&gt;2.09 ± 0.04&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;odiff www.cypress.io-1.png www.cypress.io.png www.cypress-diff.png&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;4.247 ± 0.053&lt;/td&gt;
&lt;td&gt;4.178&lt;/td&gt;
&lt;td&gt;4.344&lt;/td&gt;
&lt;td&gt;1.00&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h2&gt;
  
  
  How?
&lt;/h2&gt;

&lt;p&gt;Why it is so fast? The answer is simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It is written in OCaml and compiled to the native binary executable. OCaml compiler is extremely fast and predictable so it is easy to profile and optimize performance-sensitive code. And also we have direct node.js bindings!&lt;/li&gt;
&lt;li&gt;It is not doing a useless job under the hood. It is working directly with the low-level bytes buffer and avoids unnecessary memory allocations. &lt;/li&gt;
&lt;li&gt;It is optimized by profiling produced assembly output 👯‍♀️&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Check it out
&lt;/h2&gt;

&lt;p&gt;Try it out right now! Give us your feedback, and do not forget about ⭐️ the project if you are interested! &lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&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%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/dmtrKovalenko" rel="noopener noreferrer"&gt;
        dmtrKovalenko
      &lt;/a&gt; / &lt;a href="https://github.com/dmtrKovalenko/odiff" rel="noopener noreferrer"&gt;
        odiff
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      The fastest pixel-by-pixel image visual difference tool in the world.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&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%2Fgithub.com%2FdmtrKovalenko%2Fodiff.%2Fodiff-logo-dark.png" class="article-body-image-wrapper"&gt;&lt;img alt="pixeletad caml and odiff text with highlighted red pixels difference" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2FdmtrKovalenko%2Fodiff.%2Fodiff-logo-dark.png"&gt;&lt;/a&gt;
  
&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt; The fastest* (one-thread) pixel-by-pixel image difference tool in the world. &lt;/h3&gt;
&lt;/div&gt;

&lt;div&gt;
    &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/4716162828c4faae407147466bcd6fe51d77dde0a38e23e2f6dbe7805990743e/68747470733a2f2f666f7274686562616467652e636f6d2f696d616765732f6261646765732f6d6164652d776974682d726561736f6e2e737667"&gt;&lt;img src="https://camo.githubusercontent.com/4716162828c4faae407147466bcd6fe51d77dde0a38e23e2f6dbe7805990743e/68747470733a2f2f666f7274686562616467652e636f6d2f696d616765732f6261646765732f6d6164652d776974682d726561736f6e2e737667" alt="made with reason"&gt;&lt;/a&gt;
    &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/2f9636c9c72b5de43ba71bf995b6c5d4a76b7801aa061e75100cc514264fd4d2/68747470733a2f2f666f7274686562616467652e636f6d2f696d616765732f6261646765732f676c7574656e2d667265652e737667"&gt;&lt;img src="https://camo.githubusercontent.com/2f9636c9c72b5de43ba71bf995b6c5d4a76b7801aa061e75100cc514264fd4d2/68747470733a2f2f666f7274686562616467652e636f6d2f696d616765732f6261646765732f676c7574656e2d667265652e737667" alt="npm"&gt;&lt;/a&gt;
    &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/d3e9b70d5bcced7b64d9bd438b9f9834dec5a37cd8caa60df5a8b41da90fd978/68747470733a2f2f666f7274686562616467652e636f6d2f696d616765732f6261646765732f706f77657265642d62792d6f76657274696d652e737667"&gt;&lt;img src="https://camo.githubusercontent.com/d3e9b70d5bcced7b64d9bd438b9f9834dec5a37cd8caa60df5a8b41da90fd978/68747470733a2f2f666f7274686562616467652e636f6d2f696d616765732f6261646765732f706f77657265642d62792d6f76657274696d652e737667"&gt;&lt;/a&gt;
&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Why Odiff?&lt;/h2&gt;
&lt;/div&gt;

&lt;p&gt;ODiff is a blazing fast native image comparison tool. Check &lt;a href="https://github.com/dmtrKovalenko/odiff#benchmarks" rel="noopener noreferrer"&gt;benchmarks&lt;/a&gt; for the results, but it compares the visual difference between 2 images in &lt;strong&gt;milliseconds&lt;/strong&gt;. It was originally designed to handle the "big" images. Thanks to &lt;a href="https://ocaml.org/" rel="nofollow noopener noreferrer"&gt;OCaml&lt;/a&gt; and its speedy and predictable compiler we can significantly speed up your CI pipeline.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://vshymanskyy.github.io/StandWithUkraine/" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fvshymanskyy%2FStandWithUkraine%2Fmain%2Fbanner2-direct.svg" alt="Stand With Ukraine"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Demo&lt;/h2&gt;
&lt;/div&gt;

&lt;p&gt;&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;br&gt;
&lt;thead&gt;
&lt;br&gt;
&lt;tr&gt;
&lt;br&gt;
&lt;th&gt;base&lt;/th&gt;
&lt;br&gt;
&lt;th&gt;comparison&lt;/th&gt;
&lt;br&gt;
&lt;th&gt;diff&lt;/th&gt;
&lt;br&gt;
&lt;/tr&gt;
&lt;br&gt;
&lt;/thead&gt;
&lt;br&gt;
&lt;tbody&gt;
&lt;br&gt;
&lt;tr&gt;
&lt;br&gt;
&lt;td&gt;&lt;a rel="noopener noreferrer" href="https://github.com/dmtrKovalenko/odiffimages/tiger.jpg"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2FdmtrKovalenko%2Fodiffimages%2Ftiger.jpg" alt=""&gt;&lt;/a&gt;&lt;/td&gt;
&lt;br&gt;
&lt;td&gt;&lt;a rel="noopener noreferrer" href="https://github.com/dmtrKovalenko/odiffimages/tiger-2.jpg"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2FdmtrKovalenko%2Fodiffimages%2Ftiger-2.jpg" alt=""&gt;&lt;/a&gt;&lt;/td&gt;
&lt;br&gt;
&lt;td&gt;&lt;a rel="noopener noreferrer" href="https://github.com/dmtrKovalenko/odiffimages/tiger-diff.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2FdmtrKovalenko%2Fodiffimages%2Ftiger-diff.png" alt="1diff"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;br&gt;
&lt;/tr&gt;
&lt;br&gt;
&lt;tr&gt;
&lt;br&gt;
&lt;td&gt;&lt;a rel="noopener noreferrer" href="https://github.com/dmtrKovalenko/odiffimages/www.cypress.io.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2FdmtrKovalenko%2Fodiffimages%2Fwww.cypress.io.png" alt=""&gt;&lt;/a&gt;&lt;/td&gt;
&lt;br&gt;
&lt;td&gt;&lt;a rel="noopener noreferrer" href="https://github.com/dmtrKovalenko/odiffimages/www.cypress.io-1.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2FdmtrKovalenko%2Fodiffimages%2Fwww.cypress.io-1.png" alt=""&gt;&lt;/a&gt;&lt;/td&gt;
&lt;br&gt;
&lt;td&gt;&lt;a rel="noopener noreferrer" href="https://github.com/dmtrKovalenko/odiffimages/www.cypress-diff.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2FdmtrKovalenko%2Fodiffimages%2Fwww.cypress-diff.png" alt="1diff"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;br&gt;
&lt;/tr&gt;
&lt;br&gt;
&lt;tr&gt;
&lt;br&gt;
&lt;td&gt;&lt;a rel="noopener noreferrer" href="https://github.com/dmtrKovalenko/odiffimages/donkey.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2FdmtrKovalenko%2Fodiffimages%2Fdonkey.png" alt=""&gt;&lt;/a&gt;&lt;/td&gt;
&lt;br&gt;
&lt;td&gt;&lt;a rel="noopener noreferrer" href="https://github.com/dmtrKovalenko/odiffimages/donkey-2.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2FdmtrKovalenko%2Fodiffimages%2Fdonkey-2.png" alt=""&gt;&lt;/a&gt;&lt;/td&gt;
&lt;br&gt;
&lt;td&gt;&lt;a rel="noopener noreferrer" href="https://github.com/dmtrKovalenko/odiffimages/donkey-diff.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2FdmtrKovalenko%2Fodiffimages%2Fdonkey-diff.png" alt="1diff"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;br&gt;
&lt;/tr&gt;
&lt;br&gt;
&lt;/tbody&gt;
&lt;br&gt;
&lt;/table&gt;&lt;/div&gt;&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Features&lt;/h2&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;✅ Cross-format comparison - Yes .jpg vs .png comparison without any problems.&lt;/li&gt;
&lt;li&gt;✅ Support for &lt;code&gt;.png&lt;/code&gt;, &lt;code&gt;.jpeg&lt;/code&gt;, &lt;code&gt;.jpg&lt;/code&gt;, and &lt;code&gt;.tiff&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;✅ Supports comparison of images with different layouts.&lt;/li&gt;
&lt;li&gt;✅ Anti-aliasing detection&lt;/li&gt;
&lt;li&gt;✅ Ignoring regions&lt;/li&gt;
&lt;li&gt;✅ Using &lt;a href="https://progmat.uaem.mx/progmat/index.php/progmat/article/view/2010-2-2-03/2010-2-2-03" rel="nofollow noopener noreferrer"&gt;YIQ NTSC
transmission algorithm&lt;/a&gt; to determine visual difference.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Coming in the nearest future:&lt;/h3&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;⏹ Reading image from memory buffer&lt;/li&gt;
&lt;li&gt;⏹ Reading images from url&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Usage&lt;/h2&gt;

&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Basic comparison&lt;/h3&gt;

&lt;/div&gt;

&lt;p&gt;Run the simple comparison. Image paths can be one of supported formats, diff output can…&lt;/p&gt;
&lt;/div&gt;


&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/dmtrKovalenko/odiff" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;p&gt;Thank you for your time! Optimize your CI pipeline and in order to follow the tradition – no pixels were harmed in the making of this article 🙈&lt;/p&gt;

</description>
      <category>testing</category>
      <category>news</category>
      <category>javascript</category>
      <category>ocaml</category>
    </item>
    <item>
      <title>You might not need date-fns</title>
      <dc:creator>Dmitriy Kovalenko</dc:creator>
      <pubDate>Tue, 29 Sep 2020 22:14:58 +0000</pubDate>
      <link>https://dev.to/dmtrkovalenko/you-might-not-need-date-fns-23f7</link>
      <guid>https://dev.to/dmtrkovalenko/you-might-not-need-date-fns-23f7</guid>
      <description>&lt;p&gt;Hola! Lazy dev here and today we are going to discuss &lt;a href="https://date-fns.org/" rel="noopener noreferrer"&gt;date-fns&lt;/a&gt;. People often choose date libraries before they really need it. "How we will format the date?", "Are there any alternatives?" &lt;/p&gt;

&lt;p&gt;But really, are there? &lt;/p&gt;

&lt;h2&gt;
  
  
  Am I a hater?
&lt;/h2&gt;

&lt;p&gt;Sorry, this question was required. No, I'm not. Moreover, I was a super-active user and evangelist of date-fns. I am the creator of &lt;a href="https://github.com/dmtrKovalenko/date-io" rel="noopener noreferrer"&gt;date-io&lt;/a&gt; and &lt;a href="https://next.material-ui-pickers.dev" rel="noopener noreferrer"&gt;@material-ui/pickers&lt;/a&gt; which have been proposing to choose date-fns over the other date libraries. &lt;/p&gt;

&lt;p&gt;
  But one day I said that date-fns is not a panacea
  &lt;br&gt;
 After the twitter &lt;a href="https://twitter.com/dmtrKovalenko/status/1254414963306909699" rel="noopener noreferrer"&gt;thread&lt;/a&gt;, that was transformed in this blog post, date-fns' maintainer &lt;a href="https://twitter.com/dmtrKovalenko/status/1309854060284186630" rel="noopener noreferrer"&gt;blocked me&lt;/a&gt; everywhere because it says that you might not need it. So probably this thread contains some useful information for people that choosing date lib – so I decided to share it in the blog as well! Hope you will have some fun reading it :)  

&lt;/p&gt;

&lt;p&gt;That's it for prerequisites so let's start the discussion &lt;/p&gt;

&lt;h2&gt;
  
  
  You might not need a date library at all
&lt;/h2&gt;

&lt;p&gt;First of all, when you need to only show date values in the user-readable format – &lt;strong&gt;you can avoid date libraries at all&lt;/strong&gt;. Today all the modern browsers (even IE11) and node.js &lt;a href="https://caniuse.com/?search=datetimeformat" rel="noopener noreferrer"&gt;perfectly support&lt;/a&gt; &lt;code&gt;Intl.DateTimeFormat&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%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fu1091yrhgti9gxwdi3cr.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%2Fu1091yrhgti9gxwdi3cr.png" alt="can I use Intl.DateTimeFormat"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This means that if your task is only to show the date/time value in a user-readable format you can do the following:&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;date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;UTC&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2012&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="c1"&gt;// Ouput will depend on user locale and timezone &lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Intl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DateTimeFormat&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;It perfectly supports &lt;strong&gt;native IANA&lt;/strong&gt; timezone and locale formatting. It means that you can not include locales in the bundle at all. &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;date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;UTC&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2012&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="c1"&gt;// Results below assume UTC timezone - your results may vary&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Intl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DateTimeFormat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en-US&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="c1"&gt;// expected output: "12/20/2012"&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Intl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DateTimeFormat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fr&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="c1"&gt;// expected output: "20/12/2012"&lt;/span&gt;


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

&lt;/div&gt;
&lt;h2&gt;
  
  
  If you need more
&lt;/h2&gt;

&lt;p&gt;But there is a problem. When you need more than formatting – for example, parsing or you are working with dates too often so the native (not really best) &lt;code&gt;Date&lt;/code&gt; API is not enough. You probably will start looking for some helpful date-management library. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🌈 One wonderful day we will probably be able to get rid of all libraries in favor of native API. &lt;a href="https://github.com/tc39/proposal-temporal" rel="noopener noreferrer"&gt;Here&lt;/a&gt; is the proposal for &lt;code&gt;Temporal&lt;/code&gt;, which provides a nice functional API for working with &lt;code&gt;Date&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But today you probably will pick up date-fns. Nothing personal – only statistics. Date-fns is the most popular date library as of now. What about moment.js? It is &lt;a href="https://momentjs.com/docs/#/-project-status/" rel="noopener noreferrer"&gt;dead&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Today date-fns is much more often used for new projects. Here are &lt;a href="https://github.com/dmtrKovalenko/date-io#projects" rel="noopener noreferrer"&gt;downloads stats&lt;/a&gt; from date-io.&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%2F43uuzg4ja7hg9cc46nro.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%2F43uuzg4ja7hg9cc46nro.png" alt="date-io stats"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Statistically, you will pick date-fns. But do you &lt;strong&gt;really need&lt;/strong&gt; it? &lt;/p&gt;

&lt;p&gt;Let's discuss some criterias that are commonly used to choose the date library, and see does date-fns is the best one or not? &lt;/p&gt;
&lt;h2&gt;
  
  
  Bundlesize
&lt;/h2&gt;

&lt;p&gt;Date-fns is solving &lt;strong&gt;only 1 problem much better&lt;/strong&gt; than any other date library. And that's not a bundlesize. 🎉 Surprise 🎉 date-fns takes mostly 18kb gzip without locales. &lt;a href="https://github.com/iamkun/dayjs" rel="noopener noreferrer"&gt;Dayjs&lt;/a&gt; takes 6 (yes six kb). &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%2F3190xkekugphaemwa5cg.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%2F3190xkekugphaemwa5cg.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;
  But if compare not gzipped, but parsed size – date-fns is the biggest one
  &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%2Fumxoo13hmoshhnd7tiu1.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%2Fumxoo13hmoshhnd7tiu1.png" alt="Alt Text"&gt;&lt;/a&gt; &lt;br&gt;
&lt;/p&gt;

&lt;/p&gt;

&lt;p&gt;Ok. Ok. The problem date-fns solving REALLY nice is tree-shaking. Because each function has its own entry point and exported as esm, unused code will be removed from the bundle, right? &lt;/p&gt;
&lt;h3&gt;
  
  
  Treeshaking
&lt;/h3&gt;

&lt;p&gt;Let's create a more "real-world" example, that uses the hardest to implement manually functions: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Formatting&lt;/li&gt;
&lt;li&gt;Parsing&lt;/li&gt;
&lt;li&gt;Display time from X to Y&lt;/li&gt;
&lt;li&gt;3 locales&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  Result:
&lt;/h4&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%2Fn7tea59gefbgi5i4b35y.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%2Fn7tea59gefbgi5i4b35y.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see date-fns takes 13.88kb gzip when only importing the most important functionality. It is a lot.&lt;/p&gt;

&lt;p&gt;Here is a pretty fun example of a react datepicker that has a peer dependency on date-fns. Date-fns takes 3 times more space than the datepicker itself and mostly 1/3 size of react. And it's only to make a single date-picker work. &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%2F37ztkhqal0pd1ubkohp2.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%2F37ztkhqal0pd1ubkohp2.png" alt="react-nice dates"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Also as you saw in the bundlesize stats luxon did not change its size at all. This because luxon npm package provides only commonjs output which is not tree shakeable. So maybe one day it will become smaller. &lt;/p&gt;

&lt;p&gt;But do not forget the most wonderful thing about Luxon – it is built over native &lt;code&gt;Intl&lt;/code&gt; – so it doesn't bundle locales at all. You can support even 50 locales without any additional bundlesize for date formatting!&lt;/p&gt;

&lt;p&gt;
  P.S. All date-fns' 75 locales bundle takes 80kb gzip
  &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%2Fi%2F6r0lofncipyjgqtwtx16.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%2F6r0lofncipyjgqtwtx16.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;br&gt;


&lt;/p&gt;
&lt;h3&gt;
  
  
  Bundlesize conclusion
&lt;/h3&gt;

&lt;p&gt;Date-fns is &lt;strong&gt;not lightweight&lt;/strong&gt; library for date/time management. There are underrated alternatives – e.g. &lt;a href="https://github.com/iamkun/dayjs" rel="noopener noreferrer"&gt;Dayjs&lt;/a&gt; is much smaller when using around the same functionality. &lt;/p&gt;
&lt;h2&gt;
  
  
  API
&lt;/h2&gt;

&lt;p&gt;The next criteria for choosing a library would be the API. API must be clear, well-typed, and comprehensive. And here the most unclear for me personally – why everybody is choosing date-fns? &lt;/p&gt;

&lt;p&gt;Date-fns design is pretty straightforward – you have a separate function for everything. And this is totally perfect, but unfortunately, not for all javascript developers. The problem is that javascript doesn't have native function composition utils. &lt;/p&gt;

&lt;p&gt;I mean that some complex code with date-fns is completely unreadable: &lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;checkIsBeforeDateFns&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="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;maxTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&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="nf"&gt;isBefore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nf"&gt;setMilliseconds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;setSeconds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;setMinutes&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="mi"&gt;0&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nx"&gt;maxTime&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;Function executions need to be read like from the inside out. The first function call will be &lt;code&gt;setMinutes&lt;/code&gt; and the last will be &lt;code&gt;isBefore&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Let's compare the same functions in dayjs and luxon. They are using the old good chaining API. Most &lt;code&gt;developers&lt;/code&gt;/&lt;code&gt;editors&lt;/code&gt;/&lt;code&gt;linters&lt;/code&gt;/&lt;code&gt;static analyzers&lt;/code&gt; work like a charm with such APIs. &lt;/p&gt;

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

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;checkIsBeforeDayjs&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="nx"&gt;Dayjs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;maxTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Dayjs&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;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;minute&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;second&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;millisecond&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;isBefore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;maxTime&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;checkIsBeforeLuxon&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="nx"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;maxTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;DateTime&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;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;second&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="na"&gt;minute&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="na"&gt;millisecond&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="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;maxTime&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;Much readable right? This is actually overall a common problem in functional programming. And it can be easily fixed by using some of the function composition techniques. For example here are the same functions with &lt;a href="https://date-fns.org/v2.16.1/docs/FP-Guide" rel="noopener noreferrer"&gt;date-fns/fp&lt;/a&gt; submodule and &lt;a href="https://reasonml.github.io/" rel="noopener noreferrer"&gt;ReasonML&lt;/a&gt; (now Rescript) – native functional language compiling to javascript. And this is awesome 💜&lt;/p&gt;

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

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;checkIsBeforeDateFns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;maxTime&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="n"&gt;time&lt;/span&gt;
  &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;DateFns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setMilliseconds&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="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;DateFns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setSeconds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&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="nn"&gt;DateFns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setMinutes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&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="nn"&gt;DateFns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isBefore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;maxTime&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;This is still just 4 function calls. But much much more readable. Beautiful! &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;By the way I am the maintainer of date-fns &lt;a href="https://github.com/dmtrKovalenko/reason-date-fns" rel="noopener noreferrer"&gt;binding for ReasonML&lt;/a&gt;. Bring us a ⭐️&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But ask yourself – &lt;strong&gt;do you and &lt;em&gt;more important&lt;/em&gt; your team&lt;/strong&gt; are ready for functional programming? And do you have all the required tools for it like &lt;a href="https://www.freecodecamp.org/news/pipe-and-compose-in-javascript-5b04004ac937/" rel="noopener noreferrer"&gt;&lt;code&gt;pipe&lt;/code&gt; or &lt;code&gt;compose&lt;/code&gt;&lt;/a&gt;?&lt;/p&gt;

&lt;p&gt;If yes – take date-fns and be happy with functional programming 👨‍💻👩‍💻.&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance
&lt;/h2&gt;

&lt;p&gt;You should not think about performance before the problem was encouraged. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Premature optimization is the root of all evil © Donald Knuth&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The performance difference will be visible only on the thousand function calls per second. But if you still interesting in performance differences between date-fns and the other libraries: &lt;/p&gt;

&lt;p&gt;Short results from our &lt;a href="https://github.com/dmtrKovalenko/date-io/tree/master/packages/benchmark" rel="noopener noreferrer"&gt;date-io benchmark&lt;/a&gt;: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Date-fns is the fastest for date calculations (add, subtract, before, etc)&lt;/li&gt;
&lt;li&gt;Date-fns is the fastest for date parsing&lt;/li&gt;
&lt;li&gt;Moment is fastest for formatting (ha-ha didn't expect moment here) &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Yes, date-fns is really fast because it works directly with a native date without creating any additional wrapper. Dayjs focused on size instead of speed and Luxon is using &lt;code&gt;Intl&lt;/code&gt; which is super slow 🐌. &lt;/p&gt;

&lt;p&gt;So yes date-fns is the best option if you have performance issues with other libs. But do you really have? &lt;/p&gt;

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

&lt;p&gt;Make sure that the author of this post is incompetent, subjective, stupid, awful, and lazy. So you must reach your own conclusions for your particular project and team based on many factors. &lt;/p&gt;

&lt;p&gt;BTW &lt;a href="https://github.com/dmtrKovalenko/date-fns-comparison" rel="noopener noreferrer"&gt;here is the repo&lt;/a&gt; with all date-fns comparison stuff from this post, you can check it out, play with bundlesize and API.&lt;/p&gt;

&lt;p&gt;I will really be happy if you will think about date/time libraries in javascript and the requirement of date-fns after this reading 🤓&lt;/p&gt;

&lt;p&gt;In the author's humble opinion there are no reasons to choose date-fns if you are not cooking functional programming. And, unfortunately, as far I can see literally nobody using their really good functional approach 😿. &lt;/p&gt;

&lt;p&gt;So, in conclusion: if this lazy author one day will start a new project in javascript and will need some kind of date/time manipulations he will probably do the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Try to start with native Intl formatting&lt;/li&gt;
&lt;li&gt;When lib will become really needed choose dayjs – because its 

&lt;ul&gt;
&lt;li&gt;a) ~Harder, Better, Faster, Stronger~&lt;/li&gt;
&lt;li&gt;a) smaller&lt;/li&gt;
&lt;li&gt;b) tree shakable by plugins &lt;/li&gt;
&lt;li&gt;c) have a nice API &lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Thank you
&lt;/h2&gt;

&lt;p&gt;For this loooong read and by the tradition:&lt;/p&gt;

&lt;p&gt;No date-fns maintainers were harmed in the making of this article 😉&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>date</category>
      <category>time</category>
      <category>datefns</category>
    </item>
    <item>
      <title>The easiest way to connect cypress and react devtools 🌟</title>
      <dc:creator>Dmitriy Kovalenko</dc:creator>
      <pubDate>Wed, 23 Sep 2020 13:35:49 +0000</pubDate>
      <link>https://dev.to/dmtrkovalenko/the-easiest-way-to-connect-cypress-and-react-devtools-5hgm</link>
      <guid>https://dev.to/dmtrkovalenko/the-easiest-way-to-connect-cypress-and-react-devtools-5hgm</guid>
      <description>&lt;p&gt;Hola! Lazy dev here and today we will talk about cypress development &amp;amp; testing experience for react applications. &lt;/p&gt;

&lt;p&gt;Wondering about how to use the awesome react dev tools while testing &lt;a href="https://cypress.io" rel="noopener noreferrer"&gt;Cypress.io&lt;/a&gt;? Let's start. &lt;/p&gt;

&lt;h2&gt;
  
  
  Requirements
&lt;/h2&gt;

&lt;p&gt;Together with the v16 react team provided a completely new devtools package. You probably ❤️ love it :) &lt;/p&gt;

&lt;h3&gt;
  
  
  Install
&lt;/h3&gt;

&lt;p&gt;1) In order to run dev tools for your application running by cypress browser you need to install the standalone version of &lt;a href="https://github.com/facebook/react/tree/master/packages/react-devtools" rel="noopener noreferrer"&gt;react-devtools package&lt;/a&gt; &lt;/p&gt;

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

yarn add &lt;span class="nt"&gt;--dev&lt;/span&gt; react-devtools


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

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Tip: you can also install this package globally in order to run with different projects. It is ok because we will use this only for development. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Configure
&lt;/h3&gt;

&lt;p&gt;2) Then add this as the very first &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag in the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; of your &lt;code&gt;index.html&lt;/code&gt; (or &lt;code&gt;_document.js&lt;/code&gt; for next.js) when developing. This script actually connects react-devtools backend to the application.&lt;/p&gt;

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

&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"http://localhost:8097"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Alternatively, you can import this using javascript&lt;/p&gt;

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

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-devtools&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;⚠️ Important&lt;/strong&gt; that this import comes before any other imports in your app (especially before react-dom). Make sure to remove the import before deploying to production, as it carries a large DevTools client with it. &lt;/p&gt;

&lt;p&gt;If you use Webpack and have control over its configuration, you could alternatively add &lt;code&gt;react-devtools&lt;/code&gt; as the first item in the entry array of the &lt;strong&gt;development-only&lt;/strong&gt; configuration, and then you wouldn’t need to deal either with &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tags or import statements. &lt;/p&gt;

&lt;h2&gt;
  
  
  Running
&lt;/h2&gt;

&lt;p&gt;Now simply run your application, cypress tests, and &lt;code&gt;react-devtools&lt;/code&gt;:&lt;/p&gt;

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

&lt;span class="c"&gt;# app&lt;/span&gt;
yarn start

&lt;span class="c"&gt;# cypress &lt;/span&gt;
yarn cypress open

&lt;span class="c"&gt;# devtools app&lt;/span&gt;
yarn react-devtools 


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

&lt;/div&gt;

&lt;p&gt;(Yes, you need 3 console tabs). Running &lt;code&gt;react-devtools&lt;/code&gt; will open an electron application which will wait for the react app connection&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%2Flrcdwzuqe0fwzk2ydxm8.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%2Flrcdwzuqe0fwzk2ydxm8.png" alt="React devtools app"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And now open the cypress app, run any spec, and observe that react-devtools connected! 👀🌟&lt;/p&gt;

&lt;h2&gt;
  
  
  Result
&lt;/h2&gt;

&lt;p&gt;Fully working react devtools running over your tests in 2 minutes!&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%2Fws48aheamumt0nbwm5np.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%2Fws48aheamumt0nbwm5np.png" alt="Cypress + devtools"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Limitations
&lt;/h2&gt;

&lt;p&gt;The biggest limitation of react-devtools and cypress is that they won't work with cypress' time-traveling snapshots. This happens because separate steps of time travel are rendered as dom snapshots and are not the real react applications. &lt;/p&gt;

&lt;p&gt;In order to debug a specific test step – stop the cypress execution when you need to debug: &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%2Ffmb9mjaxng24daw0s4fj.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%2Ffmb9mjaxng24daw0s4fj.png" alt="Cypress stop button"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Alternatives
&lt;/h2&gt;

&lt;p&gt;There is another (probably more complex) way to load devtools using chrome &lt;code&gt;--load-extension=${extensionFolder}&lt;/code&gt;. This way is described in the blog post by Gleb Bahmutov &lt;a href="https://www.cypress.io/blog/2020/01/07/how-to-load-the-react-devtools-extension-in-cypress/" rel="noopener noreferrer"&gt;https://www.cypress.io/blog/2020/01/07/how-to-load-the-react-devtools-extension-in-cypress/&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The end 🎉
&lt;/h2&gt;

&lt;p&gt;Devtools works like a charm together with cypress tests. Have fun 🔥! Just don't get lost in open electron windows 😉. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;P.S. No devtools were harmed in the making of this article&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>cypress</category>
      <category>javascript</category>
      <category>testing</category>
      <category>react</category>
    </item>
    <item>
      <title>The neatest way to handle alert dialogs in React 🥰</title>
      <dc:creator>Dmitriy Kovalenko</dc:creator>
      <pubDate>Fri, 26 Jul 2019 18:55:38 +0000</pubDate>
      <link>https://dev.to/dmtrkovalenko/the-neatest-way-to-handle-alert-dialogs-in-react-1aoe</link>
      <guid>https://dev.to/dmtrkovalenko/the-neatest-way-to-handle-alert-dialogs-in-react-1aoe</guid>
      <description>&lt;p&gt;&lt;em&gt;Time to read — 5 mins&lt;/em&gt;  ☕️ &lt;/p&gt;

&lt;p&gt;Hola! Lazy dev here and we will talk about handling dialog alerts in react without tears 😢. If you are tired of tons of copy-pastes just to create new freaking «one question» modal dialog — prepare your coffee we are starting.&lt;/p&gt;

&lt;h2&gt;
  
  
  The goal
&lt;/h2&gt;

&lt;p&gt;We want to make the neatest solution for displaying an alert. Pretty similar to what we have in a browser with a native &lt;code&gt;alert&lt;/code&gt; function.&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;isConfirmed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Are you sure you want to remove this burrito?&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="nx"&gt;isConfirmed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deleteThisAwfulBurrito&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;h2&gt;
  
  
  Sneak peek
&lt;/h2&gt;

&lt;p&gt;Finally we will get to something like this. &lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;YourAwesomeComponent&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;confirm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useConfirmation&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

  &lt;span class="nf"&gt;confirm&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;danger&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Are you sure you want to remove this burrito?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;If you will remove this burrito you will regret it 😡!!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;then&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;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deleteThisAwfulBurrito&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Interested? Let's write some code. &lt;/p&gt;

&lt;p&gt;First of all, we need to start with creating actually the modal dialog. This is just a simple alert dialog built with ❤️ and &lt;a href="https://material-ui.com/components/dialogs/#dialogs" rel="noopener noreferrer"&gt;material-ui&lt;/a&gt;&lt;/p&gt;

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

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Dialog&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;DialogTitle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;DialogContent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;DialogContentText&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;DialogActions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&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;@material-ui/core&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;ConfirmationDialog&lt;/span&gt; &lt;span class="o"&gt;=&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="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;onSubmit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;onClose&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="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Dialog&lt;/span&gt; &lt;span class="na"&gt;open&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;open&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;DialogTitle&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;DialogTitle&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;DialogContent&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;DialogContentText&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;DialogContentText&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;DialogContent&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;DialogActions&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"primary"&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;onSubmit&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          YES, I AGREE
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"primary"&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;onClose&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;autoFocus&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          CANCEL
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;DialogActions&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Dialog&lt;/span&gt;&lt;span class="p"&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;OK, but how we will adopt it to be working dynamically? That's an interesting thing to consider. Why do we need &lt;strong&gt;a lot of dialogs&lt;/strong&gt; for each component if the user can see &lt;strong&gt;only one&lt;/strong&gt; alert dialog simultaneously?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you are showing an alert dialog over the other alert dialog...you probably need to reconsider the UX part of your application.&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%2Fgkx6w38p6i2dcfxw8h27.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%2Fgkx6w38p6i2dcfxw8h27.gif" alt="No god please no image"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So here we go. Everything we need is to render only &lt;strong&gt;1 top-level&lt;/strong&gt; modal at the root of our application and show it when we need to. We'll use the power of react hooks to make it looks gracefully.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap the context
&lt;/h2&gt;

&lt;p&gt;Let's create a new context instance and wrap our component tree with it. Also, create a simple state that will save the currently displaying options for the alert (like title, description and everything you need). &lt;/p&gt;

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

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;ConfirmationOptions&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&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;ConfirmationServiceContext&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;createContext&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;
  &lt;span class="c1"&gt;// we will pass the openning dialog function directly to consumers&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ConfirmationOptions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;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="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reject&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;ConfirmationServiceProvider&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&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;confirmationState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;setConfirmationState&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="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ConfirmationOptions&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;openConfirmation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ConfirmationOptions&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;setConfirmationState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&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="p"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ConfirmationServiceContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Provider&lt;/span&gt;
        &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;openConfirmation&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;

      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Dialog&lt;/span&gt; &lt;span class="na"&gt;open&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nc"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;confirmationState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;confirmationState&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&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;Now our dialog will be opened once we connect any consumer and call the provided function. &lt;/p&gt;

&lt;h2&gt;
  
  
  Resolve confirmation
&lt;/h2&gt;

&lt;p&gt;And now we need to somehow deal with closing dialog and getting a callback from the consumers. Here was used &lt;code&gt;Promise&lt;/code&gt; based API, but it is possible to make it works using a callback style. In this example, once the user accepted or canceled the alert, your awaiting promise will be resolved or rejected.&lt;/p&gt;

&lt;p&gt;To do so we need to save &lt;code&gt;Promise&lt;/code&gt;'s resolving functions and call them on appropriate user action. React's &lt;code&gt;ref&lt;/code&gt; is the best place for that.&lt;/p&gt;

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

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;awaitingPromiseRef&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="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;resolve&lt;/span&gt;&lt;span class="p"&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="k"&gt;void&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;reject&lt;/span&gt;&lt;span class="p"&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="k"&gt;void&lt;/span&gt;&lt;span class="p"&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;openConfirmation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ConfirmationOptions&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;setConfirmationState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&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;// save the promise result to the ref&lt;/span&gt;
      &lt;span class="nx"&gt;awaitingPromiseRef&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleClose&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="c1"&gt;// Mostly always you don't need to handle canceling of alert dialog&lt;/span&gt;
    &lt;span class="c1"&gt;// So shutting up the unhandledPromiseRejection errors &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;confirmationState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;catchOnCancel&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;awaitingPromiseRef&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;awaitingPromiseRef&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="nf"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;setConfirmationState&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="p"&gt;};&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleSubmit&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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;awaitingPromiseRef&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;awaitingPromiseRef&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="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;setConfirmationState&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="p"&gt;};&lt;/span&gt;



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

&lt;/div&gt;

&lt;p&gt;That's it! Our dialog machine is almost ready! One thing is left — create a custom hook for better readability &lt;/p&gt;

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

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;useConfirmationService&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="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ConfirmationServiceContext&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;


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

&lt;/div&gt;
&lt;h3&gt;
  
  
  Customization
&lt;/h3&gt;

&lt;p&gt;You can easily customize dialog content by passing additional &lt;code&gt;variant&lt;/code&gt; prop. Just add it to the &lt;code&gt;ConfirmationOptions&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;ConfirmationOptions&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;danger&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;info&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&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;And render different dialog content as you wish.&lt;/p&gt;


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

&lt;p&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;DialogActions&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;br&gt;
    &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;variant&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;danger&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;br&gt;
      &lt;span class="p"&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;br&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"primary"&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;onSubmit&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;br&gt;
          Yes, I agree&lt;br&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;br&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"primary"&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;onClose&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;autoFocus&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;br&gt;
          CANCEL&lt;br&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;br&gt;
      &lt;span class="p"&gt;&amp;lt;/&amp;gt;&lt;/span&gt;&lt;br&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;span class="si"&amp;gt;{&amp;lt;/span&amp;gt;&amp;lt;span class="nx"&amp;gt;variant&amp;lt;/span&amp;gt; &amp;lt;span class="o"&amp;gt;===&amp;lt;/span&amp;gt; &amp;lt;span class="dl"&amp;gt;"&amp;lt;/span&amp;gt;&amp;lt;span class="s2"&amp;gt;info&amp;lt;/span&amp;gt;&amp;lt;span class="dl"&amp;gt;"&amp;lt;/span&amp;gt; &amp;lt;span class="o"&amp;gt;&amp;amp;amp;&amp;amp;amp;&amp;lt;/span&amp;gt; &amp;lt;span class="p"&amp;gt;(&amp;lt;/span&amp;gt;
  &amp;lt;span class="p"&amp;gt;&amp;amp;lt;&amp;lt;/span&amp;gt;&amp;lt;span class="nc"&amp;gt;Button&amp;lt;/span&amp;gt; &amp;lt;span class="na"&amp;gt;color&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;=&amp;lt;/span&amp;gt;&amp;lt;span class="s"&amp;gt;"primary"&amp;lt;/span&amp;gt; &amp;lt;span class="na"&amp;gt;onClick&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;=&amp;lt;/span&amp;gt;&amp;lt;span class="si"&amp;gt;{&amp;lt;/span&amp;gt;&amp;lt;span class="nx"&amp;gt;onSubmit&amp;lt;/span&amp;gt;&amp;lt;span class="si"&amp;gt;}&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;&amp;amp;gt;&amp;lt;/span&amp;gt;
    OK
  &amp;lt;span class="p"&amp;gt;&amp;amp;lt;/&amp;lt;/span&amp;gt;&amp;lt;span class="nc"&amp;gt;Button&amp;lt;/span&amp;gt;&amp;lt;span class="p"&amp;gt;&amp;amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class="p"&amp;gt;)&amp;lt;/span&amp;gt;&amp;lt;span class="si"&amp;gt;}&amp;lt;/span&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;DialogActions&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;/p&gt;

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

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Are you ready?!&lt;br&gt;
&lt;/h2&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%2F54lsjtl67y6kpqttdpsv.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%2F54lsjtl67y6kpqttdpsv.jpg" alt="Are you ready kids?!"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is the final working example. Feel free to just steal the implementation of &lt;code&gt;ConfirmationService.tsx&lt;/code&gt; file if you want to. This is pretty standalone and isolated logic of what we were talking about. &lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://codesandbox.io/embed/neat-dialogs-3h5ou"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;P.S. No burritos were harmed in the making of this article  &lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>react</category>
      <category>typescript</category>
      <category>javascript</category>
      <category>architecture</category>
    </item>
  </channel>
</rss>
