<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Sebastian Weber</title>
    <description>The latest articles on Forem by Sebastian Weber (@doppelmutzi).</description>
    <link>https://forem.com/doppelmutzi</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%2F141590%2F33abf25c-60bf-4086-8cdf-85f44a181a3b.jpg</url>
      <title>Forem: Sebastian Weber</title>
      <link>https://forem.com/doppelmutzi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/doppelmutzi"/>
    <language>en</language>
    <item>
      <title>Java - Don't use raw types</title>
      <dc:creator>Sebastian Weber</dc:creator>
      <pubDate>Fri, 17 Dec 2021 12:45:56 +0000</pubDate>
      <link>https://forem.com/doppelmutzi/java-dont-use-raw-types-jn0</link>
      <guid>https://forem.com/doppelmutzi/java-dont-use-raw-types-jn0</guid>
      <description>&lt;p&gt;I'm re-learning Java, which I have already dealt with in large parts of the 2000s. In addition to pair-programming with my experienced work colleagues, I plan to pick out language features and concepts, and deal with them more intensively. In this article I describe my learnings about the chapter "Don't use raw types" part of the excellent &lt;a href="https://www.amazon.com/-/en/Joshua-Bloch/dp/0134685997/ref=sr_1_1"&gt;book "Effective Java"&lt;/a&gt;. This constitutes a good starting point to learn about &lt;a href="https://www.baeldung.com/java-generics"&gt;generics&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The key message from the article is to never ever opt-out from the generic type system. Using raw types prevents the compiler to raise errors at development time. Instead, you might come up with runtime errors. Tracking down the bug might be difficult because the issue might occur in a completely other spot away from the source of evil. &lt;/p&gt;

&lt;p&gt;The chapter is all about generics. It is a good source to learn about different concepts and terminology of generics. It focuses on the advantages of using the generic type system and contrasts the problems of raw types.&lt;/p&gt;

&lt;p&gt;It is also important to note that the examples below to discuss the topic of the book chapter are by no means good practice. There exists advanced generics features like &lt;a href="https://docs.oracle.com/javase/tutorial/java/generics/bounded.html"&gt;bounded types&lt;/a&gt; and &lt;a href="https://docs.oracle.com/javase/tutorial/java/generics/upperBounded.html"&gt;bounded wildcard types&lt;/a&gt; that should be used in professional projects. You should also never work with the type &lt;code&gt;Object&lt;/code&gt;!&lt;/p&gt;

&lt;p&gt;&lt;code&gt;List&amp;lt;E&amp;gt;&lt;/code&gt; is an example of a &lt;em&gt;generic interface&lt;/em&gt;, because it's &lt;a href="https://docs.oracle.com/javase/8/docs/api/java/util/List.html"&gt;declaration&lt;/a&gt; consists of at least one &lt;em&gt;type parameter&lt;/em&gt;, in this case &lt;code&gt;E&lt;/code&gt;. There exists generic classes, too. Both, generic interfaces and classes, can also be called &lt;em&gt;generic types&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;List&amp;lt;String&amp;gt;&lt;/code&gt; is a parameterized type, which elements are of type &lt;code&gt;String&lt;/code&gt;. Thereby, &lt;code&gt;List&lt;/code&gt; is the actual &lt;em&gt;raw type&lt;/em&gt;, &lt;code&gt;String&lt;/code&gt; represents the &lt;em&gt;type parameter&lt;/em&gt;, and &lt;code&gt;E&lt;/code&gt; constitutes the &lt;em&gt;formal type parameter&lt;/em&gt; defined in the type declaration (&lt;code&gt;List&amp;lt;E&amp;gt;&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;This article explains why you shouldn't use &lt;em&gt;raw types&lt;/em&gt; directly. The question is why does Java allow it in the first place? For compatibility reasons there still exist this concept, because generics have been first introduced with JDK 5.0. That's the reason why the compiler even with recent JDK versions erases all generic type information. So, in the end you come up with code that uses raw types directly. &lt;/p&gt;

&lt;p&gt;In fact, before the introduction of generics, there only existed raw types, so you had to use &lt;code&gt;List&lt;/code&gt; directly and cast its elements manually. It's still legal to use generic types without their type parameters in the old way.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;List&lt;/span&gt; &lt;span class="n"&gt;myHobbies&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;ArrayList&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;myHobbies&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&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;Coding&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;span class="n"&gt;myHobbies&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&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;Crossfit&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;span class="nc"&gt;Crossfit&lt;/span&gt; &lt;span class="n"&gt;crossfit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Crossfit&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;myHobbies&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works but is not recommended. The compiler points this out with &lt;em&gt;unchecked call&lt;/em&gt; warnings.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1AQcLiZD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/viurip2p0cfotmmjdizh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1AQcLiZD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/viurip2p0cfotmmjdizh.png" alt="Using raw types is legal but the compiler warns you about it."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Why is this code problematic? Because you have to know the concrete types of every item to make the correct cast. This is a real problem with dynamic code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Iterator&lt;/span&gt; &lt;span class="n"&gt;hobbiesIterator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;myHobbies&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;iterator&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="n"&gt;hobbiesIterator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;hasNext&lt;/span&gt;&lt;span class="o"&gt;();)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Crossfit&lt;/span&gt; &lt;span class="n"&gt;hobby&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Crossfit&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;hobbiesIterator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;next&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hobby&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This compiles but it leads to a runtime error due to a illegal cast.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--30AVEQSj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0q76mpr6eg7oidqe7deu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--30AVEQSj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0q76mpr6eg7oidqe7deu.png" alt="Using raw types might lead to runtime errors"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The correct way is to utilize generic type parameters in case you use generic classes or generic interfaces. Using the wrong types leads then to compiler-time errors.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Crossfit&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;moreHobbies&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;ArrayList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
&lt;span class="n"&gt;moreHobbies&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&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;Crossfit&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;span class="n"&gt;moreHobbies&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&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;Coding&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uh7omcMe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pz414p3i5kq9m1c6ytn4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uh7omcMe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pz414p3i5kq9m1c6ytn4.png" alt="Using generic type parameters does not allow for using wrong types."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But I would like to use different hobbies (i.e., different types) in my list, what can I do - yes, it's clear to me that this is a bad idea. Ok, then you better use &lt;code&gt;List&amp;lt;Object&amp;gt;&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;evenMoreHobbies&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;ArrayList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
&lt;span class="n"&gt;evenMoreHobbies&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&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;Crossfit&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;span class="n"&gt;evenMoreHobbies&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&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;Coding&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a parameterized type representing a list that can contain objects of any type. What's the difference to using the raw type &lt;code&gt;List&lt;/code&gt;? Again, you lose all the safety and expressiveness benefits that the generic type system provides. The difference is subtle. &lt;code&gt;List&amp;lt;Object&amp;gt;&lt;/code&gt; gives the compiler more information of what you have in mind as a developer: You explicitly tell the list is capable of holding any type of objects.&lt;/p&gt;

&lt;p&gt;One consequence is that the compiler does not allow to assign a list of strings to a list of objects due to sub-typing rules: &lt;code&gt;List&amp;lt;String&amp;gt;&lt;/code&gt; is a sub-type of raw type &lt;code&gt;List&lt;/code&gt; but not of the parameterized type &lt;code&gt;List&amp;lt;Object&amp;gt;&lt;/code&gt;. Thereby, the compiler guarantees that you can only add values of type &lt;code&gt;Object&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;List&lt;/span&gt; &lt;span class="n"&gt;list1&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;stringList&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;ArrayList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
&lt;span class="n"&gt;list1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stringList&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;list2&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;list2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stringList&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ffs1DAoz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fdh99rn0j4tlchj57c7o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ffs1DAoz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fdh99rn0j4tlchj57c7o.png" alt="The compiler does not allow to assign elements of another type"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's explore more what you give up when you opt-out of the generic type system, i.e., what it means to loose type-safety.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;strings&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;ArrayList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
&lt;span class="n"&gt;strings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;valueOf&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Adding a number to our string list is prevented by the compiler. This is an example for utilizing type-safety of generics.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gM_H_Udd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hnp8ow49pze9nhrmx103.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gM_H_Udd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hnp8ow49pze9nhrmx103.png" alt="The compiler does not allow to add numbers to a list of strings"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, the following &lt;code&gt;unsafeAdd&lt;/code&gt; method bypasses the type-safety mechanism.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;strings&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;ArrayList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
  &lt;span class="n"&gt;unsafeAdd&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strings&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;valueOf&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;41&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
  &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;strings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// compiler-generated cast&lt;/span&gt;
  &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hello world"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// runtime-error&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;unsafeAdd&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Object&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because we extracted the code for adding into a method that adds a &lt;code&gt;Object&lt;/code&gt; to a raw type list, we cause a runtime error.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YTa2eJCS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ouu6wbu7mk13mthlhkff.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YTa2eJCS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ouu6wbu7mk13mthlhkff.png" alt="Bypassing type-safety mechanism might lead to runtime errors"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Again, we ignore the compiler warnings and take negligently the risk of a runtime error.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5Wg_567L--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mqvv42oczrmc28jep1b2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5Wg_567L--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mqvv42oczrmc28jep1b2.png" alt="Bypassing type-safety mechanism only emits warnings but not errors"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you are in the situation that you have a method to work on a list (or other raw type) whose element types are unknown or you don't care about them, you better use &lt;a href="https://docs.oracle.com/javase/tutorial/extra/generics/wildcards.html"&gt;unbounded wildcard types&lt;/a&gt; (&lt;code&gt;List&amp;lt;?&amp;gt;&lt;/code&gt;). So the major reason to use the wildcard version is to maintain type-safety.&lt;/p&gt;

&lt;p&gt;You might ask, what's the difference between &lt;code&gt;List&amp;lt;Object&amp;gt;&lt;/code&gt; and &lt;code&gt;List&amp;lt;?&amp;gt;&lt;/code&gt;?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;objList&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;ArrayList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;?&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;wildcardList&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;ArrayList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;();&lt;/span&gt;
&lt;span class="n"&gt;objList&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// works&lt;/span&gt;
&lt;span class="n"&gt;wildcardList&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;&lt;span class="c1"&gt;// compile-time error&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You cannot assign a value to a list of &lt;em&gt;wildcard type&lt;/em&gt;. But you can leverage it for type-save checks. Consider the following code, where a &lt;code&gt;Stats&lt;/code&gt; class with a &lt;em&gt;bounded type&lt;/em&gt; (&lt;code&gt;T extends Number&lt;/code&gt;) is defined to calculate the average of numeric values.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Stats&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;T&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="no"&gt;T&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nc"&gt;Stats&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;T&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;numbers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="nf"&gt;average&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;].&lt;/span&gt;&lt;span class="na"&gt;doubleValue&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;sameAverage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Stats&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;?&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;average&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;average&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;Integer&lt;/span&gt; &lt;span class="n"&gt;intNumbers&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="o"&gt;};&lt;/span&gt;
  &lt;span class="nc"&gt;Stats&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;intStats&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;Stats&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;intNumbers&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="nc"&gt;Double&lt;/span&gt; &lt;span class="n"&gt;doubleNumbers&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="mf"&gt;2.0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;4.0&lt;/span&gt; &lt;span class="o"&gt;};&lt;/span&gt;
  &lt;span class="nc"&gt;Stats&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Double&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;doubleStats&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;Stats&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;doubleNumbers&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

  &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"same average? "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;intStats&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sameAverage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;doubleStats&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
  &lt;span class="c1"&gt;// System.out.println("same average? " + intStats.sameAverage(23)); // compile-time error&lt;/span&gt;
  &lt;span class="c1"&gt;// System.out.println("same average? " &lt;/span&gt;
  &lt;span class="c1"&gt;// + intStats.sameAverage(new Stats&amp;lt;String&amp;gt;("hello world"))); // compile-time error&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The method &lt;code&gt;sameAverage&lt;/code&gt; makes use of a &lt;em&gt;wildcard type&lt;/em&gt;. You cannot pass anything into the method other than a numeric value (i.e., a sub-class of &lt;code&gt;Number&lt;/code&gt;). You have to understand that the wildcard does not affect what type of &lt;code&gt;Stats&lt;/code&gt; objects can be created or what actual type can be passed to the &lt;code&gt;sameAverage&lt;/code&gt; method. The class declaration (&lt;code&gt;T extends Number&lt;/code&gt;) does this.&lt;/p&gt;

&lt;p&gt;A key take-away of this article is to never use raw types and instead use the generic type system. Then, the compiler has the possibilities to find wrong type assignments already at development time.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Efficient Code Analyzing and Formatting (for React) with ESLint, Prettier and VSCode: 2020 Edition</title>
      <dc:creator>Sebastian Weber</dc:creator>
      <pubDate>Mon, 24 Feb 2020 22:20:38 +0000</pubDate>
      <link>https://forem.com/doppelmutzi/efficient-code-analyzing-and-formatting-for-react-with-eslint-prettier-and-vscode-2020-edition-36n5</link>
      <guid>https://forem.com/doppelmutzi/efficient-code-analyzing-and-formatting-for-react-with-eslint-prettier-and-vscode-2020-edition-36n5</guid>
      <description>&lt;p&gt;Two years ago, I wrote an article on how to setup a development workflow utilizing &lt;a href="https://eslint.org/"&gt;ESLint&lt;/a&gt; for static code analysis and &lt;a href="https://prettier.io/"&gt;Prettier&lt;/a&gt; for beautifying code. It was in the context of &lt;a href="https://vuejs.org/"&gt;Vue.js&lt;/a&gt; but 99% of the concepts and related technologies are independent of the actual frontend development framework. Meanwhile, I use &lt;a href="https://reactjs.org/"&gt;React&lt;/a&gt; on a daily basis on my job project.&lt;/p&gt;

&lt;p&gt;As you most likely know, things are steadily changing in "frontend world". That's why I come up with a 2020 remake of &lt;a href="https://medium.com/@doppelmutzi/eslint-prettier-vue-workflow-46a3cf54332f"&gt;my original article (which targeted Vue.js – but it doesn't matter)&lt;/a&gt;. However, I do not want to rehash the old article by duplicating chapters. Instead, I focus only on the technical details:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;related npm dependencies&lt;/li&gt;
&lt;li&gt;general ESLint / Prettier config&lt;/li&gt;
&lt;li&gt;React related config&lt;/li&gt;
&lt;li&gt;VSCode integration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you are interested in the underlying concepts, please feel free to check out &lt;a href="https://medium.com/@doppelmutzi/eslint-prettier-vue-workflow-46a3cf54332f"&gt;my former article&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What is ESLint, what is Prettier, how they differ&lt;/li&gt;
&lt;li&gt;How to integrate ESLint with Prettier&lt;/li&gt;
&lt;li&gt;What are possible workflows (terminal, git hooks, IDE, etc.)&lt;/li&gt;
&lt;li&gt;IntelliJ integration&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  React Project Setup
&lt;/h1&gt;

&lt;p&gt;The easiest thing to launch a running React project is to use &lt;a href="https://github.com/facebook/create-react-app"&gt;create-react-app&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npx create-react-app react-project
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Go to the &lt;code&gt;package.json&lt;/code&gt; file, remove the &lt;code&gt;eslint-config&lt;/code&gt; object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"eslintConfig"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"extends"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"react-app"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Add the following &lt;code&gt;devDependencies&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"devDependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"babel-eslint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^10.0.3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"eslint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^6.8.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"eslint-config-babel"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^9.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"eslint-config-prettier"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^6.10.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"eslint-plugin-import"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^2.20.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"eslint-plugin-prettier"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^3.1.2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"eslint-plugin-react"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^7.18.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"eslint-plugin-standard"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^4.0.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"prettier"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^1.19.1"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You can learn more about this dependencies by &lt;a href="https://medium.com/@doppelmutzi/eslint-prettier-vue-workflow-46a3cf54332f#b33f"&gt;reading my old article&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TLTR;&lt;/strong&gt;&lt;br&gt;
With this setup you can integrate ESLint with Prettier in a way that code formatting is turned off for ESLint (&lt;a href="https://github.com/prettier/eslint-config-prettier"&gt;eslint-config-prettier&lt;/a&gt;) and runs Prettier as an ESLint rule (&lt;a href="https://github.com/prettier/eslint-plugin-prettier"&gt;eslint-plugin-prettier&lt;/a&gt;. &lt;a href="https://github.com/standard/eslint-plugin-standard"&gt;eslint-plugin-standard&lt;/a&gt; and &lt;a href="https://github.com/yannickcr/eslint-plugin-react"&gt;eslint-plugin-react&lt;/a&gt; are two of the supported plugins.&lt;/p&gt;

&lt;p&gt;Of course you have to install the dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npm i
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The final step is to add a &lt;code&gt;.eslintrc.json&lt;/code&gt; file to the root folder with the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"extends"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"prettier"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"prettier/standard"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"prettier/react"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"plugin:react/recommended"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"plugins"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"prettier"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"rules"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"prettier/prettier"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"error"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"parser"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"babel-eslint"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"browser"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"settings"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"react"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"pragma"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"React"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"detect"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h1&gt;
  
  
  Required Visual Studio Extensions
&lt;/h1&gt;

&lt;p&gt;You need to install two VSCode extensions to get a pretty awesome developer UX. First, you have to install the &lt;a href="https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode"&gt;Prettier plugin&lt;/a&gt;. In addition, you need the &lt;a href="https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint"&gt;ESLint plugin&lt;/a&gt; as well. Just search in the extension section for the keywords &lt;em&gt;"eslint"&lt;/em&gt; and &lt;em&gt;"prettier"&lt;/em&gt; and make sure to install the correct ones, from Dirk Baeumer and Esben Petersen, respectively.&lt;/p&gt;

&lt;p&gt;A nice feature of VSCode is to &lt;a href="https://code.visualstudio.com/docs/editor/extension-gallery#_workspace-recommended-extensions"&gt;add recommended extensions to your project&lt;/a&gt;. It can be useful to get new developers productive fast. All you need to to is to add &lt;code&gt;.vscode&lt;/code&gt; folder to the root of your project and add a &lt;code&gt;extensions.json&lt;/code&gt; file with the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"recommendations"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"dbaeumer.vscode-eslint"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; 
    &lt;/span&gt;&lt;span class="s2"&gt;"esbenp.prettier-vscode"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;For your colleagues it then looks like this.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--IZg0GL9V--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/rs0mgmaleywo8thv61qa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IZg0GL9V--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/rs0mgmaleywo8thv61qa.png" alt="VSCode recommended extensions"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Setup Format on Save
&lt;/h1&gt;

&lt;p&gt;The final part of setting up VSCode is to provide the auto save configuration in the workspace settings. Fire up the &lt;a href="https://code.visualstudio.com/docs/getstarted/userinterface#_command-palette"&gt;command palette&lt;/a&gt; with ⇧⌘P and type "open workspace settings". Switch to the json editor. Put the following code inside the json file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"editor.codeActionsOnSave"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"source.fixAll.eslint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"editor.formatOnSave"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"[javascript]"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"editor.formatOnSave"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"[javascriptreact]"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"editor.formatOnSave"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The last two properties are necessary for me to turn of other formatting mechanism that have nothing to do with ESLint or Prettier.&lt;/p&gt;

&lt;p&gt;By now I think it is a good idea to have these settings under version control. This workspace settings are located in &lt;code&gt;.vscode/settings.json&lt;/code&gt; of your project. It can help to eliminate different VSCode setups among your colleagues. At least it saves time for recurring work.&lt;/p&gt;

&lt;p&gt;At the latest after a restart of VSCode format on save should work as expected for JSX and javascript code as well as json and css files.&lt;/p&gt;

&lt;h1&gt;
  
  
  Demo Project
&lt;/h1&gt;

&lt;p&gt;You can check out my demo project to see all this in action. Just perform the following steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/doppelmutzi/eslint-prettier-vscode-react.git"&gt;clone&lt;/a&gt; the project&lt;/li&gt;
&lt;li&gt;install the dependencies
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Optional: Install the VSCode extensions for ESLint and Prettier. Go to the Extension section, enter &lt;em&gt;@recommended&lt;/em&gt; into the &lt;em&gt;Search Extensions in Marketplace&lt;/em&gt; search field, and install both extensions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Go to javascript, json or css files, change them and press save. The file should be reformatted.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;The setup became even easier than 2 years ago. However, many vectors change, so that it is always a bit complicated to find the interaction between all technologies. In addition, you can find outdated information on the net, which does not make life any easier. To fix the problem for February 2020, this article was created 😀.&lt;/p&gt;

&lt;p&gt;Cover image by &lt;a href="https://unsplash.com/@frantic"&gt;Alex Kotliarskyi&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>react</category>
      <category>eslint</category>
      <category>prettier</category>
      <category>vscode</category>
    </item>
    <item>
      <title>Upgrade macOS to Utilize Keyboard Shortcuts for a more Productive Development Workflow</title>
      <dc:creator>Sebastian Weber</dc:creator>
      <pubDate>Tue, 19 Nov 2019 21:37:48 +0000</pubDate>
      <link>https://forem.com/doppelmutzi/upgrade-macos-to-utilize-keyboard-shortcuts-for-a-more-productive-development-workflow-41hl</link>
      <guid>https://forem.com/doppelmutzi/upgrade-macos-to-utilize-keyboard-shortcuts-for-a-more-productive-development-workflow-41hl</guid>
      <description>&lt;p&gt;Using a mouse or trackpad is a major distraction and flow-breaker for many development tasks, especially programming. I strive for using keyboard shortcuts as much as possible to be more productive while programming.&lt;/p&gt;

&lt;p&gt;Out of the box, macOS does not provide many keyboard shortcuts when it comes to window management. As an example, there is no way to move a focused window to another space by keyboard.&lt;/p&gt;

&lt;p&gt;In this article I want to present my curated list of useful tools to manage windows, displays, and spaces (of &lt;a href="https://en.wikipedia.org/wiki/Mission_Control_(macOS)"&gt;Mission Control&lt;/a&gt;) without a mouse. I hope you retire your mouse after reading this post. 😉&lt;/p&gt;

&lt;h1&gt;
  
  
  Alfred – Application Launcher on Steroids
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://www.alfredapp.com/"&gt;Alfred&lt;/a&gt; is an elite application launcher and utility to search and find files on your machine or on the Web. I use Alfred instead of &lt;a href="https://support.apple.com/en-us/HT204014"&gt;Spotlight&lt;/a&gt;. The hotkey to open up the launcher panel has been burned into my brain for a long time. Of course, you can define a custom key combo in Alfred's settings.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--f_SmxQFi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://doppelmutzi.github.io/images/mac-productivity-utilities/alfred-hotkey.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--f_SmxQFi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://doppelmutzi.github.io/images/mac-productivity-utilities/alfred-hotkey.png" alt="You can define a custom Alfred hotkey to activate"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Alfred is very versatile, you just have to fire up the panel with your defined hotkey and you can start typing. Most of the time I type the starting characters of an app I want to launch. However, you can do more from this input field, e.g., start a google search as you can see from the next screenshot (marked by &lt;strong&gt;⌘5&lt;/strong&gt;).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ydIL6Scm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://doppelmutzi.github.io/images/mac-productivity-utilities/alfred-search.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ydIL6Scm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://doppelmutzi.github.io/images/mac-productivity-utilities/alfred-search.png" alt="Launch apps or search for content"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To open a file or folder on your machine, start typing &lt;strong&gt;open&lt;/strong&gt; and Alfred assists you with recommended actions shown in the result list.&lt;/p&gt;

&lt;p&gt;Alfred has many more awesome features like snippets or workflows, which I will not cover here. Take a look at Alfred's &lt;a href="https://www.alfredapp.com/powerpack/"&gt;power pack&lt;/a&gt;. I want to point out one awesome feature and that is &lt;strong&gt;Clipboard History&lt;/strong&gt;. You can define a shortcut in the &lt;strong&gt;Features&lt;/strong&gt; section of Alfred's settings.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1Woj0eJJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://doppelmutzi.github.io/images/mac-productivity-utilities/alfred-clipboard.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1Woj0eJJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://doppelmutzi.github.io/images/mac-productivity-utilities/alfred-clipboard.png" alt="You can define a clipboard history hotkey to invoke the associated panel"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you press the defined hotkey, a clipboard history panel appears and allows you to paste more than the last copied item.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WaOZID_m--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://doppelmutzi.github.io/images/mac-productivity-utilities/alfred-clipboard-history.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WaOZID_m--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://doppelmutzi.github.io/images/mac-productivity-utilities/alfred-clipboard-history.png" alt="Clipboard history allows for pasting more than the last copied item"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Spectacle – The Missing Window Resizer
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://www.spectacleapp.com/"&gt;Spectacle&lt;/a&gt; is an awesome tiny tool that allows for changing the size or position of a particular window. It upgrades macOS in a way that you can use keyboard shortcuts to resize or move focused windows of any app. Sophisticated window management by shortcuts is one aspect I have missed from Microsoft Windows when I was working on a project with PCs as development machines.&lt;/p&gt;

&lt;p&gt;In my current project, I have an environment with two external displays in addition to the built-in display of my MacBook. Spectacle provides&lt;br&gt;
customizable shortcuts even for throwing a focused window to another display. Therefore, you can specify shortcuts in Spectacle's settings dialog for &lt;strong&gt;Next Display&lt;/strong&gt; and &lt;strong&gt;Previous Display&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--AIBoGhzp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://doppelmutzi.github.io/images/mac-productivity-utilities/spectacle-settings.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AIBoGhzp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://doppelmutzi.github.io/images/mac-productivity-utilities/spectacle-settings.jpg" alt="Settings of Spectacle"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Besides moving windows from one visible display to another, I highly use shortcuts to resize windows like &lt;strong&gt;Make Larger&lt;/strong&gt;, &lt;strong&gt;Make Smaller&lt;/strong&gt;, or &lt;strong&gt;Left Half&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I appreciate Spectacle's simplicity. You have a nice glasses icon in the menu bar to see all window actions along with keyboard shortcuts at a glance.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--m7JS1FPw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://doppelmutzi.github.io/images/mac-productivity-utilities/spectacle-menubar.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--m7JS1FPw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://doppelmutzi.github.io/images/mac-productivity-utilities/spectacle-menubar.png" alt="Easy access from menu bar entry"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The following recording gives an impression how Spectacle works. I used the shortcuts as you can see in the screenshot above.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/CZNxXPJ2k44"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Spectacle is brilliant, however, it lacks one feature I really need. I need to move windows also to invisible spaces. Spectacle only allows to move windows between visible spaces (i.e., displays) by defining shortcuts for &lt;strong&gt;Next Display&lt;/strong&gt; and &lt;strong&gt;Previous Display&lt;/strong&gt;. Luckily, the next tool can help out.&lt;/p&gt;

&lt;h1&gt;
  
  
  Amethyst – A Handy Tiling Window Manager
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://ianyh.com/amethyst/"&gt;Amethyst&lt;/a&gt; constitutes a tiling window manager for macOS. It has some features in common with Spectacle, e.g., increasing and decreasing window sizes by shortcuts. However, as you can see from the screenshot about Amethyst's settings, the tool provides many more useful shortcuts.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--depMLT0T--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://doppelmutzi.github.io/images/mac-productivity-utilities/amethyst.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--depMLT0T--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://doppelmutzi.github.io/images/mac-productivity-utilities/amethyst.png" alt="Shortcuts section of Amethyst"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My personal killer feature is the ability to throw focused windows to a particular space. I very often move a window to the left or right space (&lt;strong&gt;Throw focused window to space left&lt;/strong&gt; or &lt;strong&gt;Throw focused window to space right&lt;/strong&gt;).&lt;/p&gt;

&lt;p&gt;Another very handy feature does not require using shortcuts at all. Amethyst rearranges windows on a display automatically (i.e., visible space in focus). As an example, if you have a single browser window on your active display and you open another browser window by &lt;strong&gt;⌘ N&lt;/strong&gt;, both windows are positioned next to each other with the exact same width and height. If you put another window of any software on this display, all three windows are rearranged again in a way that there is no overlapping. Wicked!&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/8DPpZ0LodeU"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h1&gt;
  
  
  Contexts – An Improved Command-Tab Switcher
&lt;/h1&gt;

&lt;p&gt;I use &lt;a href="https://contexts.co/"&gt;Contexts&lt;/a&gt; primarily for an improved "Command-Tab switcher".&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1N8PcU4N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://doppelmutzi.github.io/images/mac-productivity-utilities/contexts.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1N8PcU4N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://doppelmutzi.github.io/images/mac-productivity-utilities/contexts.png" alt="Better Command-Tab Switcher"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Contexts adds the ability to select individual windows by using the learned key combos &lt;strong&gt;⌘ ⇥&lt;/strong&gt; and &lt;strong&gt;⇧ ⌘ ⇥&lt;/strong&gt; (to move up the list). In the settings you can also define other shortcuts for these actions. Additionally, you can also use multiple shortcuts for invoking the window switcher with individual settings, which is not possible with macOS default switcher.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9oGGFSug--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://doppelmutzi.github.io/images/mac-productivity-utilities/contexts-settings.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9oGGFSug--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://doppelmutzi.github.io/images/mac-productivity-utilities/contexts-settings.png" alt="Define multiple shortcuts for window switching"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see from the previous screenshot, you can define any number of shortcuts to activate the window switcher and to move the list up and down. What I really like is that you can restrict the list for every shortcut, e.g., show only visible spaces.&lt;/p&gt;

&lt;p&gt;Another major time saver is Contexts' search capability. Actually, it acts more like a filter for the window list. When you have activated the switcher panel, you can just start typing (with pressed hotkey) and, thereby, filter the list to save the time for moving up and down the list.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--sd-JUXpA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://doppelmutzi.github.io/images/mac-productivity-utilities/contexts-search.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--sd-JUXpA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://doppelmutzi.github.io/images/mac-productivity-utilities/contexts-search.png" alt="Even faster window selection by searching"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can even optimize this workflow by defining a modifier key to activate and use the fast search feature. To switch to a window, press this modifier key and type a few characters from the app name or window title. As you can see from a previous screenshot, on the left-hand side of the switcher panel characters are shown that show you suitable strings for fast search.&lt;/p&gt;

&lt;p&gt;What I also like is that I can remove particular apps from the switcher panel's list, e.g., macOS Finder, because I'm not convinced of this tool and use instead an alternative.&lt;/p&gt;

&lt;h1&gt;
  
  
  macOS Build-in Shortcuts
&lt;/h1&gt;

&lt;p&gt;There are some shortcuts to use &lt;a href="https://support.apple.com/en-us/HT204100"&gt;Mission Control&lt;/a&gt; without a mouse. For me, the most important one is to switch between spaces by pressing &lt;strong&gt;⌃ ←&lt;/strong&gt; or &lt;strong&gt;⌃ →&lt;/strong&gt;. To open Mission Control or show its spaces bar press &lt;strong&gt;⌃ ↑&lt;/strong&gt;. To show all windows of an application side by side press &lt;strong&gt;⌃ ↓&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Of course, the following shortcuts should become second nature if you want to be productive on a Mac. &lt;strong&gt;⌘ Q&lt;/strong&gt; quits applications along with all its windows. To close a focused window press &lt;strong&gt;⌘ W&lt;/strong&gt;. To copy and paste things use &lt;strong&gt;⌘ C&lt;/strong&gt; and &lt;strong&gt;⌘ V&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Another useful shortcut during my office hours is locking the screen by pressing &lt;strong&gt;⌃ ⌘ Q&lt;/strong&gt;. In addition, a useful shortcut to kill none-responding applications is &lt;strong&gt;⌥ ⌘ ESC&lt;/strong&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  KeyCue – Shortcus Always at a Glance
&lt;/h1&gt;

&lt;p&gt;If you cannot remember these system shortcuts, you can take a look at the application menu. If a command provides a shortcut, you can find it on the right side of the menu entry. An even faster approach is to use &lt;a href="https://www.ergonis.com/products/keycue/"&gt;KeyCue&lt;/a&gt;. It's a useful tool to show all shortcuts of the current application which is currently in focus along with system shortcuts. The goal of KeyCue is to find, remember, and learn keyboard shortcuts.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GHj7lMPx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://doppelmutzi.github.io/images/mac-productivity-utilities/keycue.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GHj7lMPx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://doppelmutzi.github.io/images/mac-productivity-utilities/keycue.png" alt="Find, remenber, and learn shortcuts with KeyCue"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can show the list of shortcuts for a focused app from the keyboard. As an example, I configured KeyCue to show the list by pressing the &lt;strong&gt;⌘&lt;/strong&gt; key for 1.5 seconds.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--y4SYWbql--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://doppelmutzi.github.io/images/mac-productivity-utilities/keycue-settings.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--y4SYWbql--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://doppelmutzi.github.io/images/mac-productivity-utilities/keycue-settings.png" alt="Activate KeyCue with hotkeys"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Summary
&lt;/h1&gt;

&lt;p&gt;I want to point out that the presented tools provide way more features. However, these are the most important capabilities for me to reduce mouse usage.&lt;/p&gt;

&lt;p&gt;Resizing and moving windows between visible and none-visible spaces with just a few keystrokes has significantly improved my productivity during a course of a day. I hope I could provide you some inspiration to improve your development workflow.&lt;/p&gt;

</description>
      <category>macos</category>
      <category>shortcuts</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Yarn Workspaces does not Honor .npmrc Location Precedence: Implications and Possible Solutions</title>
      <dc:creator>Sebastian Weber</dc:creator>
      <pubDate>Fri, 04 Oct 2019 14:33:48 +0000</pubDate>
      <link>https://forem.com/doppelmutzi/yarn-workspaces-does-not-honor-npmrc-location-precedence-implications-and-possible-solutions-1jog</link>
      <guid>https://forem.com/doppelmutzi/yarn-workspaces-does-not-honor-npmrc-location-precedence-implications-and-possible-solutions-1jog</guid>
      <description>&lt;p&gt;Yarn Workspaces has a bug that does not respect the location precedence of .npmrc / .yarnrc files to configure registry settings if you &lt;a href="https://yarnpkg.com/lang/en/docs/cli/workspace/"&gt;run a yarn command in a selected workspace&lt;/a&gt;. Consider the following situation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;em&gt;.npmrc&lt;/em&gt; file located at home folder specifies a &lt;a href="https://docs.npmjs.com/configuring-your-registry-settings-as-an-npm-enterprise-user"&gt;registry&lt;/a&gt; entry to use a private npm registry.&lt;/li&gt;
&lt;li&gt;A &lt;em&gt;.npmrc&lt;/em&gt; file located at a project root specifies a registry entry to target a public npm registry like this &lt;code&gt;registry=https://registry.npmjs.org&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In my current project, I have such a situation. The default situation is that projects need a registry setup to use the internal &lt;a href="https://jfrog.com/artifactory/"&gt;Artifactory&lt;/a&gt;. One project requires a setup to target the public npm registry. The problem of this project is that adding a dependency to a specific yarn workspaces package with the following command uses the wrong registry setup (the setup of &lt;code&gt;~/.npmrc&lt;/code&gt; instead of &lt;code&gt;.npmrc&lt;/code&gt; file located at project root):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;yarn workspace package-a add @rooks/use-previous
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The problem is that wrong URLs are put into the &lt;code&gt;yarn.lock&lt;/code&gt; file (targeting the private registry).&lt;/p&gt;

&lt;p&gt;However, if you add a dependency globally with the &lt;code&gt;-W&lt;/code&gt; flag, then the &lt;code&gt;.npmrc&lt;/code&gt; precedence is honored:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;yarn add @rooks/use-previous &lt;span class="nt"&gt;-W&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This bug seems to exist &lt;a href="https://github.com/yarnpkg/yarn/issues/4458"&gt;for a very long time&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The following workarounds are possible:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use the &lt;code&gt;--registry&lt;/code&gt; flag
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;yarn workspace package-a add @rooks/use-previous &lt;span class="nt"&gt;--registry&lt;/span&gt; &lt;span class="s1"&gt;'https://registry.yarnpkg.com'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Manually add the dependency to the &lt;code&gt;package.json&lt;/code&gt; of &lt;em&gt;package-a&lt;/em&gt; and run &lt;code&gt;yarn install&lt;/code&gt; from the root folder of the project.&lt;/li&gt;
&lt;li&gt;Copy &lt;code&gt;~/.npmrc&lt;/code&gt; to every project root folder that need this registry setup and delete &lt;code&gt;~/.npmrc&lt;/code&gt;. If you have private settings (e.g., your user credentials) in the file, pay attention that you do not push the file to VCS (add it to &lt;code&gt;.gitignore&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Don't use yarn workspaces. E.g., use &lt;a href="https://github.com/lerna/lerna"&gt;Lerna&lt;/a&gt; with npm.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>yarn</category>
      <category>yarnworkspaces</category>
      <category>troubleshooting</category>
    </item>
    <item>
      <title>Testing of a Custom React Hook for Fetching Data with Axios</title>
      <dc:creator>Sebastian Weber</dc:creator>
      <pubDate>Sun, 14 Jul 2019 09:59:49 +0000</pubDate>
      <link>https://forem.com/doppelmutzi/testing-of-a-custom-react-hook-for-fetching-data-with-axios-4gf1</link>
      <guid>https://forem.com/doppelmutzi/testing-of-a-custom-react-hook-for-fetching-data-with-axios-4gf1</guid>
      <description>&lt;p&gt;Hooks is a new concept of React. It requires some rethinking of existing knowledge. Furthermore, developing React components with hooks requires a mind shift (e.g., &lt;a href="https://overreacted.io/a-complete-guide-to-useeffect/" rel="noopener noreferrer"&gt;don't think in lifecycle methods&lt;/a&gt;). It needs some time to get comfortable, but with some practice hooks can be incorporated into real-life projects without problems. Custom hooks are very useful to encapsulate logic into isolated modules that can be easily reused.&lt;/p&gt;

&lt;p&gt;However, testing hooks is (currently) no easy task. It took me quite some time to write working tests for my custom hooks. This post describes the crucial aspects for testing them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhu4i3x4vjyogufgs0kw1.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhu4i3x4vjyogufgs0kw1.jpg" alt="Jooks is a library stating that React Hooks are f*ing hard to test." width="800" height="290"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can find the code for the custom hook as well as the corresponding tests in my &lt;a href="https://codesandbox.io/s/testing-custom-hook-demo-ko6bq" rel="noopener noreferrer"&gt;Code Sandbox&lt;/a&gt;.&lt;br&gt;
&lt;iframe src="https://codesandbox.io/embed/testing-custom-hook-demo-ko6bq"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h1&gt;
  
  
  The Custom Hook
&lt;/h1&gt;

&lt;p&gt;This article expects you to know how to write custom React hooks. If you are new to this topic, check out &lt;a href="https://reactjs.org/docs/hooks-custom.html" rel="noopener noreferrer"&gt;React's documentation&lt;/a&gt;. Another good starting point is to take a look at &lt;a href="https://github.com/rehooks/awesome-react-hooks" rel="noopener noreferrer"&gt;awesome-react-hooks&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The following code snippet constitutes a simple custom hook to perform a &lt;em&gt;GET&lt;/em&gt; request with &lt;em&gt;axios&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// useFetch.js&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;useState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useEffect&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&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="nx"&gt;axios&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;axios&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// custom hook for performing GET request&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;useFetch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;initialValue&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="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;initialValue&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;loading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fetchData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;setLoading&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nf"&gt;setData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&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;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="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="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;setLoading&lt;/span&gt;&lt;span class="p"&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="p"&gt;};&lt;/span&gt;
    &lt;span class="nf"&gt;fetchData&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;url&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;useFetch&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The following code shows how this custom hook can be used.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./styles.css&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="nx"&gt;useFetch&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;./useFetch&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;loading&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="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useFetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://jsonplaceholder.typicode.com/posts/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;App&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;loader&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&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;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
        &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;blog&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;blog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&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;blog&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="err"&gt;}
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rootElement&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;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;ReactDOM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;App&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;rootElement&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Testing the Custom Hook
&lt;/h1&gt;

&lt;p&gt;At the time of this writing, testing hooks is no straight forward task. React's official documentation provides only a &lt;a href="https://reactjs.org/docs/hooks-faq.html#how-to-test-components-that-use-hooks" rel="noopener noreferrer"&gt;tiny section on this topic&lt;/a&gt;. I had a hard time to test hooks because of violations against the &lt;a href="https://reactjs.org/docs/hooks-rules.html" rel="noopener noreferrer"&gt;rules of hooks&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;However, I've discovered &lt;a href="https://react-hooks-testing-library.com/" rel="noopener noreferrer"&gt;react-hooks-testing-library&lt;/a&gt; that handles running hooks within the body of a function component, as well as providing various useful utility functions.&lt;/p&gt;

&lt;p&gt;Before you write your tests, you need to install the library along with its peer dependencies as &lt;a href="https://react-hooks-testing-library.com/setup" rel="noopener noreferrer"&gt;described in the documentation&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npm i &lt;span class="nt"&gt;-D&lt;/span&gt; @testing-library/react-hooks
&lt;span class="nv"&gt;$ &lt;/span&gt;npm i react@^16.8.0
&lt;span class="nv"&gt;$ &lt;/span&gt;npm i &lt;span class="nt"&gt;-D&lt;/span&gt; react-test-renderer@^16.8.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The custom hook utilizes &lt;a href="https://www.npmjs.com/package/axios" rel="noopener noreferrer"&gt;axios&lt;/a&gt; for fetching data. We need a way to mock the actual networking. There are many ways to do this. I like &lt;a href="https://github.com/ctimmerm/axios-mock-adapter" rel="noopener noreferrer"&gt;axios-mock-adapter&lt;/a&gt; making it easy to write tests for successful and failing requests. You need to install these libraries, too.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npm i axios
&lt;span class="nv"&gt;$ &lt;/span&gt;npm i &lt;span class="nt"&gt;-D&lt;/span&gt; axios-mock-adapter
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First, take a look at the following &lt;a href="https://jestjs.io/" rel="noopener noreferrer"&gt;Jest&lt;/a&gt; test, before we discuss the crucial parts.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// useFetch.test.js&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;renderHook&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;@testing-library/react-hooks&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="nx"&gt;axios&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;axios&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="nx"&gt;MockAdapter&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;axios-mock-adapter&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="nx"&gt;useFetch&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;./useFetch&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;useFetch performs GET request&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;initialValue&lt;/span&gt; &lt;span class="o"&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;mock&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;MockAdapter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;axios&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;mockData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;response&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;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://mock&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;mockData&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;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;waitForNextUpdate&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;renderHook&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;useFetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;initialValue&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;result&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="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toEqual&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;result&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="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBeTruthy&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;waitForNextUpdate&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;result&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="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;response&lt;/span&gt;&lt;span class="dl"&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;result&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="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBeFalsy&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;The implementation of &lt;em&gt;useFetch&lt;/em&gt; performs a network request with &lt;em&gt;axios&lt;/em&gt;. Therefore, we mock the &lt;em&gt;GET&lt;/em&gt; request before we call &lt;em&gt;useFetch&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mock&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;MockAdapter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="cm"&gt;/* 
  Mock network call. Instruct axios-mock-adapter 
  to return with expected data and status code of 200.
*/&lt;/span&gt;
&lt;span class="nx"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;mockData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// invoke our custom hook&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;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;waitForNextUpdate&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;renderHook&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;useFetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;initialValue&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;As you can see, &lt;em&gt;useFetch&lt;/em&gt; is wrapped in a &lt;a href="https://react-hooks-testing-library.com/usage/basic-hooks#rendering" rel="noopener noreferrer"&gt;renderHook&lt;/a&gt; function invocation. What this actually does is to provide the correct context to execute the custom hook without violating the rules of hooks (in this case that &lt;a href="https://react-hooks-testing-library.com/#the-problem" rel="noopener noreferrer"&gt;hooks can only be called inside the body of a function component&lt;/a&gt;). &lt;/p&gt;

&lt;p&gt;The &lt;em&gt;renderHook&lt;/em&gt; call returns a &lt;a href="https://react-hooks-testing-library.com/reference/api#renderhook" rel="noopener noreferrer"&gt;RenderHookResult&lt;/a&gt;. In our example, we destructure &lt;em&gt;result&lt;/em&gt; and &lt;em&gt;waitForNextUpdate&lt;/em&gt; from the result object. Let's discuss &lt;em&gt;result&lt;/em&gt; first.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ...&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;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;waitForNextUpdate&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;renderHook&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;useFetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;initialValue&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;result&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="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toEqual&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;result&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="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBeTruthy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;result&lt;/em&gt; constitutes the &lt;a href="https://react-hooks-testing-library.com/reference/api#renderhook-result" rel="noopener noreferrer"&gt;renderHook result&lt;/a&gt;. As you can see in the &lt;em&gt;expect&lt;/em&gt; statement, we can access the actual return value of our custom hook from &lt;em&gt;result.current&lt;/em&gt;.  So &lt;em&gt;result.current.data&lt;/em&gt; and &lt;em&gt;result.current.loading&lt;/em&gt; hold the return value of the custom hook call. These two assertions evaluate to true. The &lt;em&gt;data&lt;/em&gt; state holds the passed initial value and the &lt;em&gt;loading&lt;/em&gt; state is true because the actual network call has not been performed yet. &lt;/p&gt;

&lt;p&gt;So far, so good, but how do we perform the call? Therefore, we need &lt;em&gt;waitForNextUpdate&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&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;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;waitForNextUpdate&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;renderHook&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;useFetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;initialValue&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;result&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="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toEqual&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;result&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="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBeTruthy&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;waitForNextUpdate&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;result&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="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;response&lt;/span&gt;&lt;span class="dl"&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;result&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="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBeFalsy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;waitForNextUpdate&lt;/em&gt; allows us to wait for the asynchronous function to return in order to check the response of the network call.&lt;/p&gt;

&lt;p&gt;The following extract is from the &lt;a href="https://react-hooks-testing-library.com/usage/advanced-hooks#async" rel="noopener noreferrer"&gt;lib's documentation&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;[...] returns a Promise that resolves the next time the hook renders, commonly when state is updated as the result of an asynchronous action [...].&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After &lt;code&gt;await waitForNextUpdate()&lt;/code&gt; returns we can safely assert that &lt;em&gt;result.current.data&lt;/em&gt; holds data coming from the (mocked) network request. In addition, a state change by calling &lt;code&gt;setLoading(false)&lt;/code&gt; was performed and, thus, &lt;em&gt;result.current.loading&lt;/em&gt; is &lt;em&gt;false&lt;/em&gt;. &lt;/p&gt;

&lt;h1&gt;
  
  
  Testing More Use Cases
&lt;/h1&gt;

&lt;p&gt;In the following, you see a code snippet with two additional tests. The first one tests if our hook implementation can handle multiple invocations. The second one checks the network error case with the help of &lt;em&gt;axios-mock-adapter&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;useFetch performs multiple GET requests for different URLs&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="c1"&gt;// fetch 1&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;initialValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;initial 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;mock&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;MockAdapter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;axios&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;mockData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&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;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://mock&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;mockData&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;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;waitForNextUpdate&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;renderHook&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;useFetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;initialValue&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;result&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="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;initial 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;expect&lt;/span&gt;&lt;span class="p"&gt;(&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;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBeTruthy&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;waitForNextUpdate&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;result&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="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&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;result&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="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBeFalsy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// fetch 2&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://mock2&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;mockData2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url2&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;mockData2&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;initialValue2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;initial value 2&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="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;result2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;waitForNextUpdate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;waitForNextUpdate2&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;renderHook&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="nf"&gt;useFetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;initialValue2&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;result2&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="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;initial value 2&lt;/span&gt;&lt;span class="dl"&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;result2&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="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBeTruthy&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;waitForNextUpdate2&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;result2&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="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&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;result2&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="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBeFalsy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;useFetch sets loading to false and 
returns inital value on network error&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mock&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;MockAdapter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;axios&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;initialValue&lt;/span&gt; &lt;span class="o"&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;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://mock&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nx"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;networkError&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;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;waitForNextUpdate&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;renderHook&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;useFetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;initialValue&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;result&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="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toEqual&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;result&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="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBeTruthy&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;waitForNextUpdate&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;result&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="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBeFalsy&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;result&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="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toEqual&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;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;I really like the API of &lt;em&gt;react-hooks-testing-library&lt;/em&gt;. But what I like most is that the library enables me to test custom hooks in the first place. IMHO testing with this lib is straightforward.&lt;/p&gt;

&lt;p&gt;If you see annoying warnings in the console as shown in the following screenshot, chances are high that you can fix it by updating your dependencies.&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1xbvkw8j58gbpfj7dd2n.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1xbvkw8j58gbpfj7dd2n.jpg" alt="Annoying warnings in the console output." width="800" height="947"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The act warning has been resolved with the react@^16.9.0 and @testing-library/react-hooks@^2.0.0 releases. &lt;/p&gt;

</description>
      <category>react</category>
      <category>hooks</category>
      <category>jest</category>
      <category>axios</category>
    </item>
    <item>
      <title>How are CSS Styles Applied to HTML Elements? Understand the Cascade!</title>
      <dc:creator>Sebastian Weber</dc:creator>
      <pubDate>Tue, 28 May 2019 21:57:58 +0000</pubDate>
      <link>https://forem.com/doppelmutzi/understand-how-css-styles-are-applied-to-html-elements-by-learning-the-fundamentals-precedence-specificity-cascade-de4</link>
      <guid>https://forem.com/doppelmutzi/understand-how-css-styles-are-applied-to-html-elements-by-learning-the-fundamentals-precedence-specificity-cascade-de4</guid>
      <description>&lt;p&gt;Normally, beginning your journey with CSS entails a steep learning curve. However, mastering CSS is hard since it is such a broad topic in itself. The field of applications is diverse, thus understanding the basics helps you to become a more productive developer.&lt;/p&gt;

&lt;p&gt;Sure, having a more or less understanding is most of the time sufficient. Applying the trial and error method until your selectors do what you want does most of the time the job. However, in big projects you will sooner or later struggle with side effects or other weird bugs without understanding precedence in CSS.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/yYSSBtDgbbRzq/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/yYSSBtDgbbRzq/giphy.gif" alt="Frustrated CSS programming animated gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Precedence
&lt;/h1&gt;

&lt;p&gt;As a reminder, take a look at the structure of a CSS rule.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9iijopr4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://developer.mozilla.org/%40api/deki/files/6167/%3Dcss_syntax_-_ruleset.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9iijopr4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://developer.mozilla.org/%40api/deki/files/6167/%3Dcss_syntax_-_ruleset.png" alt="CSS ruleset consists of group of selectors and a declarations block"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If several CSS rules target the same HTML element and these rules constitute definitions with one or more mutual CSS properties, which styles are applied by the browser in the end? This is where CSS precedence comes into play.&lt;/p&gt;

&lt;p&gt;Examples help to make complex situations more understandable. Take a look at the following code snippets (or play around with the &lt;a href="https://codepen.io/doppelmutzi/pen/yWLbbL"&gt;Codepen&lt;/a&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- HTML template code --&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;h2&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"subline"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;subline&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;aside&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;sidebar title&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/aside&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* some styles to target h2 elements */&lt;/span&gt;

&lt;span class="nc"&gt;.subline&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;serif&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;h2&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;sans-serif&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;aside&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;red&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;In the end, the sidebar title element has a sans-serif and red font applied. The subline element has a black serif font.&lt;/p&gt;

&lt;p&gt;Several interesting things happened here. There is no selector that explicitly target a &lt;code&gt;h2&lt;/code&gt; element to assign a font color. However, the red color of the sidebar title was inherited by its parent element (&lt;code&gt;aside&lt;/code&gt;). The second CSS &lt;code&gt;font-family&lt;/code&gt; declaration (&lt;code&gt;h2&lt;/code&gt; selector) was not applied by the subline element because the &lt;code&gt;.subline&lt;/code&gt; selector was considered as more specific by the browser.&lt;/p&gt;

&lt;p&gt;All these aspects are considered to determine which CSS declarations for every DOM element have to be applied. If multiple CSS declarations (e.g., &lt;code&gt;font-family: serif;&lt;/code&gt; and &lt;code&gt;font-family: sans-serif;&lt;/code&gt;) with the same CSS property (&lt;code&gt;color&lt;/code&gt;) are found to target a particular DOM element (e.g., the sidebar title), the browser creates a precedence order. Thereby, the property value with the highest precedence gets applied.&lt;/p&gt;

&lt;p&gt;As you can see from this simple example, many aspects play a role in the browser process of determining the actual CSS styles. To be more specific, the following concepts are part of the precedence algorithm:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;specificity&lt;/li&gt;
&lt;li&gt;inheritance&lt;/li&gt;
&lt;li&gt;the cascade&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Specificity
&lt;/h1&gt;

&lt;p&gt;Take a look at the following &lt;a href="https://codepen.io/doppelmutzi/pen/WZavOm"&gt;CSS snippet&lt;/a&gt;. It shows a bunch of selectors to style &lt;code&gt;li&lt;/code&gt; elements.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;
&lt;span class="nc"&gt;.challenge&lt;/span&gt; &lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="nd"&gt;::before&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;'😃'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;margin-right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;.25em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;vertical-align&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;middle&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.challenge&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;ul&lt;/span&gt; &lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="nd"&gt;::before&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;'😴'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;data-smiley&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;'crying'&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="nd"&gt;::before&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;'😭'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The corresponding HTML elements are shown next.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;main&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"challenge"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;ul&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"list"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;1.1&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;ul&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"list"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;2.1&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;2.2&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;ul&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"list"&lt;/span&gt; &lt;span class="na"&gt;data-smiley=&lt;/span&gt;&lt;span class="s"&gt;"crying"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;3.1&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;3.2&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;3.3&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;2.3&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;1.2&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;1.3&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/main&amp;gt;&lt;/span&gt;

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



&lt;p&gt;If you specify multiple selectors to target the same HTML element, the browser picks the selector with the highest &lt;a href="https://www.w3.org/TR/selectors-3/#specificity"&gt;specificity&lt;/a&gt; value.&lt;/p&gt;

&lt;p&gt;What is selector specificity all about? Specificity constitutes the amount of importance each CSS declaration block has in comparison with others based on what its selector is made up of. It is important to understand that specificity only relates to the selector. This means, the actual CSS declaration block is irrelevant in the context of specificity. However, specificity values are calculated by the cascade algorithm to bring these CSS declaration blocks in some kind of importance order. Based on this, styles are determined to apply to HTML elements.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://specificity.keegan.st/"&gt;Specificity Calculator&lt;/a&gt; is a nice online tool constituting a visual way to understand this concept.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NMxyALjf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/y1azmd2bqqd6wq51i3iy.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NMxyALjf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/y1azmd2bqqd6wq51i3iy.jpg" alt="specificity"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The first selector has a higher specificity because classes are more specific than elements.&lt;/p&gt;

&lt;p&gt;You can think of selector specificity as a row vector of 4 elements as depicted by the &lt;a href="https://css-tricks.com/wp-content/csstricks-uploads/specificity-calculationbase.png"&gt;illustration of CSS Tricks&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TIfsgPwL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://css-tricks.com/wp-content/csstricks-uploads/specificity-calculationbase.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TIfsgPwL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://css-tricks.com/wp-content/csstricks-uploads/specificity-calculationbase.png" alt="Representation of selector specificity as row vector"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Consider the following examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;selector 1 (0,1,0,0) wins over selector 2 (0,0,0,41)&lt;/li&gt;
&lt;li&gt;selector 1 (0,2,0,1) wins over selector 2 (0,1,2,15)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Take a look at the following example that consist of two special cases.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;  &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;red&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="nf"&gt;#title&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nt"&gt;h1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;green&lt;/span&gt; &lt;span class="cp"&gt;!important&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;The universal selector (&lt;code&gt;*&lt;/code&gt;) has a specificity of (0,0,0,0). The &lt;code&gt;!important&lt;/code&gt; keyword beats everything, thus use it with care (or better don't use it). In the example, every &lt;code&gt;h1&lt;/code&gt; element has a green color, even the element with the id &lt;code&gt;title&lt;/code&gt;. To override such a declaration, your only chance is to specify another declaration with &lt;code&gt;!important&lt;/code&gt;, so order is relevant.&lt;/p&gt;

&lt;p&gt;Over 10 years ago, Andy Clarke published an awesome &lt;a href="https://stuffandnonsense.co.uk/archives/css_specificity_wars.html"&gt;article explaining selector specificity through Star Wars&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MbG9zBMp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/3dms0tmqdmqp43j30mj7.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MbG9zBMp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/3dms0tmqdmqp43j30mj7.jpg" alt="selector specificity explained through star wars"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is a fun way to learn the concept of specificity. I also recommend Emma Wedekind's &lt;a href="https://dev.to/emmawedekind/css-specificity-1kca"&gt;in-depth explanation of CSS specificity&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Inheritance
&lt;/h1&gt;

&lt;p&gt;Inheritance controls what happens if no value for a CSS property has been defined for an element. To be more precise, the inheritance mechanism propagates CSS property values from a parent element to its child elements. However, not every property value gets inherited. MDN's CSS property reference can be used to find out if the values of a particular property gets inherited or not. As you can see from in the following screenshot, &lt;code&gt;margin&lt;/code&gt; does not get inherited by default.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jz4T2Ys3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/vhpat6ktn8xz0julo6my.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jz4T2Ys3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/vhpat6ktn8xz0julo6my.png" alt="MDN's CSS property reference for margin"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A &lt;a href="https://www.w3.org/TR/CSS21/propidx.html"&gt;comprehensive list of all CSS properties&lt;/a&gt; with information about inheritance provides W3C.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ikyHb1xW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/nsbjfhgi86n0eaylzbdm.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ikyHb1xW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/nsbjfhgi86n0eaylzbdm.jpg" alt="Comprehensive list of CSS properties by W3C"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Consider the following example.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;article&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Title&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Lorem Ipsum&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/article&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;article&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;p&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;inherit&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;By using the &lt;code&gt;inherit&lt;/code&gt; keyword, the &lt;code&gt;margin&lt;/code&gt; property value of the &lt;code&gt;article&lt;/code&gt; element gets propagated to the &lt;code&gt;p&lt;/code&gt; element. In contrast, the &lt;code&gt;h2&lt;/code&gt; element does not inherit the &lt;code&gt;margin&lt;/code&gt; value of its parent since &lt;code&gt;margin&lt;/code&gt; does not get inherited by default.&lt;/p&gt;

&lt;p&gt;The following &lt;a href="https://codepen.io/doppelmutzi/pen/NaOGER?editors=1100"&gt;Codepen&lt;/a&gt; constitutes a more comprehensive example of CSS inheritance.&lt;/p&gt;

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

&lt;h1&gt;
  
  
  The Cascade
&lt;/h1&gt;

&lt;p&gt;CSS stands for &lt;em&gt;Cascading Style Sheets&lt;/em&gt;, so it is not surprising that the cascade plays an important role. This algorithm calculates the above explained precedence for all CSS declarations. Thereby, it also considers specificity and inheritance to determine which styles are applied to every HTML element of your HTML document.&lt;/p&gt;

&lt;p&gt;The simplified algorithm looks like this (for complete details refer &lt;a href="https://www.w3.org/TR/CSS2/cascade.html#cascade"&gt;W3C specification&lt;/a&gt;). The algorithm is executed for every HTML element:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Collect every CSS declaration that comes into question for the current HTML element.&lt;/li&gt;
&lt;li&gt;Sort these declarations by &lt;em&gt;origin&lt;/em&gt; and &lt;em&gt;weight&lt;/em&gt;.

&lt;ul&gt;
&lt;li&gt;Origin refers to the location where the declaration is specified (e.g., inline styles as &lt;code&gt;style&lt;/code&gt; attribute or within an externally defined stylesheet file).&lt;/li&gt;
&lt;li&gt;Weight equals importance of the declaration, i.e., author styles (styles that we developer provide) &amp;gt; user styles (styles specified by end-users, e.g., &lt;a href="https://davidwalsh.name/firefox-user-stylesheet"&gt;in Firefox&lt;/a&gt;) &amp;gt; browser defaults (e.g., most desktop browsers define &lt;code&gt;16px&lt;/code&gt; as default &lt;code&gt;font-size&lt;/code&gt; for &lt;code&gt;html&lt;/code&gt; elements).&lt;/li&gt;
&lt;li&gt;The following rules are valid for &lt;em&gt;author styles&lt;/em&gt;: inline styles &amp;gt; styles defined within the &lt;code&gt;head&lt;/code&gt; element &amp;gt; styles part of external files&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;!importance&lt;/code&gt; (e.g., &lt;code&gt;p { color: red !important; }&lt;/code&gt;) constitutes a higher weight than normal CSS declarations.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Sort all selectors targeting the current HTML element by specificity values (the highest value on top of the list).&lt;/li&gt;
&lt;li&gt;Are two CSS declarations equal regarding all rules above, the declaration wins that is specified later in terms of document flow. So &lt;em&gt;order&lt;/em&gt; acts as tie-breaker in such situations. Order comprises the location where styles are integrated: integrated external stylesheets within the &lt;code&gt;head&lt;/code&gt; element but also imported stylesheets (with &lt;code&gt;@import&lt;/code&gt; declaration) from within external stylesheet files.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The following &lt;a href="https://srjcstaff.santarosa.edu/~tfleming/htmlb/CSS_Cheat_Sheet_Inheritance_Cascade_Specificity.pdf"&gt;illustration&lt;/a&gt; explains the algorithm in a nutshell.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uO9rb1pM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/hjvljlbpjwp83y9bavcd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uO9rb1pM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/hjvljlbpjwp83y9bavcd.png" alt="Cascade algorithm explained as an illustration"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's briefly discuss how the cascade effects on inheritance. What happens if you have two ancestors of an element with the same properties that gets propagated down the document tree due to inheritance? Ultimately, the property values of the ancestor that is closest to the element are applied.&lt;/p&gt;

&lt;p&gt;If you would like to have another perspective on the cascade, I recommend &lt;a href="https://blog.logrocket.com/how-css-works-understanding-the-cascade-d181cd89a4d8"&gt;Benjamin Johnson's article&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Utilize browser dev tools
&lt;/h1&gt;

&lt;p&gt;Developer tools of modern browsers are very helpful to grasp the concept explained in this article. The next annotated screenshot shows how information regarding inheritance and precedence (crossed out declarations, origin of styles, etc.) are visualized by Chrome dev tools.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--m7PeluzN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/zpscnw37lc6adq8y9wfj.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--m7PeluzN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/zpscnw37lc6adq8y9wfj.jpg" alt="Screenshot of Chrome dev tools with annotations to show how information about inheritance and precedence are visualized"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This picture constitutes a screenshot of a Codepen with annotations to show why which CSS declaration is applied or not applied by the algorithm. Concrete, the annotations represent the analysis for the &lt;code&gt;&amp;lt;h2 class=”subline”&amp;gt;Subline&amp;lt;/h2&amp;gt;&lt;/code&gt; element (marked by blue arrow).&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion and Lessons Learned
&lt;/h1&gt;

&lt;p&gt;The cascade is the system managing styles from multiple sources and determines what declarations take precedence in case of conflicts. The cascade algorithm considers styles that are directly applied to HTML elements as well as styles for HTML elements that are not explicitly defined (i.e., inherited styles).&lt;/p&gt;

&lt;p&gt;Especially for beginners, it might not be clear that the cascade "collect" styles from different sources: explicit defined, inherited, default styles, etc.&lt;/p&gt;

&lt;p&gt;IMHO a solid understanding of the cascade algorithm is the key for becoming a better Web developer. If you understand how the browser applies styles to HTML elements, you can avoid frustration in development projects (e.g., unwanted applied styles or side effects).&lt;/p&gt;

&lt;p&gt;It turns out that the cascade algorithm is actually easy to understand and memorize. Most of the time, you only deal with author styles so the number of rules to remember is not that large. If you are familiar with &lt;em&gt;specificity&lt;/em&gt; and &lt;em&gt;inheritance&lt;/em&gt; you are good to go. Further, if you have a good CSS design you do not have to think much about document flow because you most likely do not spread styles all over your stylesheet files.&lt;/p&gt;

</description>
      <category>css</category>
      <category>precedence</category>
      <category>specificity</category>
      <category>cascade</category>
    </item>
    <item>
      <title>Why does React Hooks enable writing simpler code? 🎣</title>
      <dc:creator>Sebastian Weber</dc:creator>
      <pubDate>Thu, 23 May 2019 06:40:58 +0000</pubDate>
      <link>https://forem.com/doppelmutzi/why-do-react-hooks-enable-writing-simpler-code-oh6</link>
      <guid>https://forem.com/doppelmutzi/why-do-react-hooks-enable-writing-simpler-code-oh6</guid>
      <description>&lt;p&gt;While learning React Hooks I have read a lot about the conceptual decisions of the React team. E.g., they state that React components with React Hooks are easier to write and understand as Class components.&lt;/p&gt;

&lt;p&gt;Why do you think writing projects with React Hooks will lead to more understandable and maintainable code?&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>react</category>
    </item>
    <item>
      <title>Master Flexbox Scaling by Understanding flex-grow, flex-shrink, and flex-basis</title>
      <dc:creator>Sebastian Weber</dc:creator>
      <pubDate>Wed, 15 May 2019 18:45:58 +0000</pubDate>
      <link>https://forem.com/doppelmutzi/master-flexbox-scaling-by-understanding-flex-grow-flex-shrink-and-flex-basis-5dlf</link>
      <guid>https://forem.com/doppelmutzi/master-flexbox-scaling-by-understanding-flex-grow-flex-shrink-and-flex-basis-5dlf</guid>
      <description>&lt;p&gt;Inspired by an article why &lt;a href="https://css-tricks.com/flex-grow-is-weird/" rel="noopener noreferrer"&gt;flex-grow is weird&lt;/a&gt;, I've developed a &lt;a href="https://qqq3mv5rvw.codesandbox.io/" rel="noopener noreferrer"&gt;small tool (Codesandbox)&lt;/a&gt; to explore how different properties of flex items work separately or in combination. You can reproduce all examples of this article with my tool. I hope you find it useful.&lt;/p&gt;

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

&lt;p&gt;In case this tool is not intuitive for you, this article breaks down different combination of &lt;a href="https://www.w3.org/TR/css-flexbox-1/#flex-basis-property" rel="noopener noreferrer"&gt;flex-basis&lt;/a&gt;, &lt;a href="https://www.w3.org/TR/css-flexbox-1/#flex-grow-property" rel="noopener noreferrer"&gt;flex-grow&lt;/a&gt;, and &lt;a href="https://www.w3.org/TR/css-flexbox-1/#flex-shrink-property" rel="noopener noreferrer"&gt;flex-shrink&lt;/a&gt; to show how these flexibility properties can be leveraged for responsive flexbox layouts.&lt;/p&gt;

&lt;h2&gt;
  
  
  flex-basis
&lt;/h2&gt;

&lt;p&gt;Take a look at the next screenshot.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2rujkblvheapd9rfbbgd.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2rujkblvheapd9rfbbgd.jpg" alt="screenshot of tool with set flex-basis and width properties" width="800" height="541"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;First of all, the blue annotations explain how the tool works. In this example, the 2 flex items (blue = flex item 1 and green = flex item 2) do not consume the whole available space of the flex container (width of &lt;code&gt;500px&lt;/code&gt;).  &lt;code&gt;100px&lt;/code&gt; free space is represented by 2 grey-scaled squares (500px / 10 segments). The grey-scaled ruler is meant as utility to visualize how many "portions" of the flex container are free for distribution.&lt;/p&gt;

&lt;p&gt;The next example shows both &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;flex-basis&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4uxy3nrlskz4m90u4pbo.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4uxy3nrlskz4m90u4pbo.jpg" alt="width is omitted if flex-basis is specified" width="800" height="651"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So what is the difference between &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;flex-basis&lt;/code&gt; In case you specify both properties (green flex item), &lt;code&gt;width&lt;/code&gt; is ignored. The blue flex item does not specify &lt;code&gt;flex-basis&lt;/code&gt; so the item automatically gets the default property (&lt;code&gt;flex-basis: auto&lt;/code&gt;). In such a case, &lt;code&gt;width&lt;/code&gt; is used for scaling.&lt;/p&gt;

&lt;p&gt;Dave Geddes compiled an in-depth explanation on &lt;a href="https://gedd.ski/post/the-difference-between-width-and-flex-basis/" rel="noopener noreferrer"&gt;the difference between width and flex-basis&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  flex-shrink
&lt;/h2&gt;

&lt;p&gt;As you can see in the next example, &lt;code&gt;flex-basis&lt;/code&gt; can cause an overflow of the flex container.&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe3h9b9cv4avji876pf92.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe3h9b9cv4avji876pf92.jpg" alt="flex-basis causes overflow on flexbox container" width="800" height="309"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, specifying a &lt;code&gt;flex-shrink&lt;/code&gt; value fixes the problem by allowing the blue flex item to shrink.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0hcxklc5xcf7kt67lr6f.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0hcxklc5xcf7kt67lr6f.jpg" alt="flex-basis with flex-shrink fixes overflow" width="800" height="362"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can specify &lt;code&gt;flex-shrink&lt;/code&gt; for every flex item. In the next example, flex item 1 shrinks 4 times more than flex item 2.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fihwpwkypkskpi0lmqcn9.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fihwpwkypkskpi0lmqcn9.jpg" alt="flex item 1 shrinks 4 times more than flex item 1" width="800" height="663"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, the blue flex item consumes 1 spot (&lt;code&gt;100px&lt;/code&gt;) and the green flex item consumes 4 spots (&lt;code&gt;400px&lt;/code&gt;). How come?&lt;/p&gt;

&lt;p&gt;The following code snippet shows the calculation for the available space for the aforementioned example.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// pseudo algorithm for flex-shrink&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;flexItem1Width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;flexItem2Width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;flexContainerWidth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;
&lt;span class="c1"&gt;// 500 - 500 + 500 = | -500 | = 500px&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;spaceToDistribute&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;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;flexContainerWidth&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;flexItem1Width&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;flexItem2Width&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;flexItem1ShrinkFactor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;flexItem2ShrinkFactor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="c1"&gt;// 5&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;totalShrinkValues&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;flexItem1ShrinkFactor&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;flexItem2ShrinkFactor&lt;/span&gt;
&lt;span class="c1"&gt;// 100px&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;distributionSpot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;spaceToDistribute&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;totalShrinkValues&lt;/span&gt;
&lt;span class="c1"&gt;// 500px - (4 * 100px) = 100px&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;flexItem1ComputedWidth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="nx"&gt;flexItem1Width&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;flexItem1ShrinkFactor&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;distributionSpot&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;// 500px - (1 * 100px) = 400px&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;flexItem2ComputedWidth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="nx"&gt;flexItem2Width&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;flexItem2ShrinkFactor&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;distributionSpot&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Downsizing only happens if required. In the following example, the &lt;code&gt;flex-basis&lt;/code&gt; values are respected.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp061bia82igo6vo4utcu.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp061bia82igo6vo4utcu.jpg" alt="Specified flex-shrink but no effect" width="800" height="502"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  flex-grow
&lt;/h2&gt;

&lt;p&gt;Of course, you can specify multiple flex item properties. The next screenshot shows that providing &lt;code&gt;flex-shrink&lt;/code&gt; and &lt;code&gt;flex-grow&lt;/code&gt; values is totally legit.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr37s5iajumuxhal2swwj.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr37s5iajumuxhal2swwj.jpg" alt="Specified flex-shrink and flex-grow properties" width="800" height="653"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this case, only &lt;code&gt;flex-grow&lt;/code&gt; values take effect causing both flex items to grow equally (because of same factor &lt;code&gt;1&lt;/code&gt;). Obviously, you can also specify different &lt;code&gt;flex-grow&lt;/code&gt; values for your flex items and the behavior is exactly as you expect it (same as with &lt;code&gt;flex-shrink&lt;/code&gt; but growing instead of shrinking).&lt;/p&gt;

&lt;p&gt;Finally, let's take a look at the following example to examine the calculation formula for &lt;code&gt;flex-grow&lt;/code&gt;, too.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ficoztt7qb6qe7s22t3uq.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ficoztt7qb6qe7s22t3uq.jpg" alt="2 different flex-grow properties" width="800" height="659"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The algorithm is nearly the same as for &lt;code&gt;flex-shrink&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// pseudo code for flex-grow algorithm&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;flexItem1Width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;flexItem2Width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;
&lt;span class="c1"&gt;// 10px + 50px - 500px = | -440px | = 440px&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;spaceToDistribute&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;440&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;flexItem1GrowFactor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;flexItem2GrowFactor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;
&lt;span class="c1"&gt;// 15&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;totalGrowValues&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;flexItem1GrowFactor&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;flexItem2GrowFactor&lt;/span&gt;
&lt;span class="c1"&gt;// 440px / 15 = 29,3333&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;distributionSpot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;spaceToDistribute&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;totalGrowValues&lt;/span&gt;
&lt;span class="c1"&gt;// 10px + (10 * 29,3333px) = 303,33333px&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;flexItem1ComputedWidth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="nx"&gt;flexItem1Width&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;flexItem1GrowFactor&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;distributionSpot&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;// 50px + (5 * 29,3333px) = 196,6666px&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;flexItem2ComputedWidth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="nx"&gt;flexItem2Width&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;flexItem2GrowFactor&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;distributionSpot&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  min-width and max-width
&lt;/h2&gt;

&lt;p&gt;Let's take the last example and specify &lt;code&gt;max-width&lt;/code&gt; values for both flex items, too.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffrmz8zleys5wc0u0cg8f.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffrmz8zleys5wc0u0cg8f.jpg" alt="flex-grow with max-width properties" width="800" height="655"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, &lt;code&gt;max-width&lt;/code&gt; values are respected. That's why flex item 1 does not grow beyond &lt;code&gt;100px&lt;/code&gt; (it does not scale to &lt;code&gt;303,333px&lt;/code&gt;). In addition, flex item 2 does not scale beyond &lt;code&gt;200px&lt;/code&gt; (it does not scale to &lt;code&gt;196,6666px&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Finally, let's see how different flex item properties behave.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feh2ulwxmuc1jzwc8s47p.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feh2ulwxmuc1jzwc8s47p.jpg" alt="combination of many flex item properties" width="800" height="657"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this example, flex item 1 cannot consume its &lt;a href="https://gedd.ski/post/the-difference-between-width-and-flex-basis/" rel="noopener noreferrer"&gt;hypothetical main size&lt;/a&gt; of &lt;code&gt;600px&lt;/code&gt;. Due to the fact that a &lt;code&gt;flex-shrink&lt;/code&gt; value other than &lt;code&gt;0&lt;/code&gt; or &lt;code&gt;auto&lt;/code&gt; is specified, the blue flex item is allowed to shrink. However, it does not shrink beyond its &lt;code&gt;min-width&lt;/code&gt; value (&lt;code&gt;200px&lt;/code&gt;). In the end, its width is &lt;code&gt;300px&lt;/code&gt; and that's because of the properties of flex item 2. The green flex item cannot take its &lt;code&gt;flex-basis&lt;/code&gt; width (&lt;code&gt;250px&lt;/code&gt;). Due to &lt;code&gt;flex-grow&lt;/code&gt; and &lt;code&gt;flex-shrink&lt;/code&gt; values the item is allowed to shrink and grow. Growing is not possible because there is no available free space within the flex container. So, the &lt;code&gt;max-width&lt;/code&gt; value is irrelevant. However, &lt;code&gt;min-width&lt;/code&gt; is respected and the green flex item takes up &lt;code&gt;200px&lt;/code&gt; space.&lt;/p&gt;

&lt;h2&gt;
  
  
  flex Shorthand
&lt;/h2&gt;

&lt;p&gt;Manuel Matuzovic &lt;a href="https://css-tricks.com/flex-grow-is-weird/" rel="noopener noreferrer"&gt;explains in detail&lt;/a&gt; why &lt;code&gt;flex-grow: 1&lt;/code&gt; is not the same as &lt;code&gt;flex: 1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is because &lt;code&gt;flex&lt;/code&gt; is the shorthand for &lt;code&gt;flex-grow&lt;/code&gt;, &lt;code&gt;flex-shrink&lt;/code&gt;, and &lt;code&gt;flex-basis&lt;/code&gt;. It is tricky because you can &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/flex#Syntax" rel="noopener noreferrer"&gt;use it with one, two, and three values&lt;/a&gt;. E.g., if you use only one value, the other values are implicitly set (e.g., by default values).&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary and Lessons Learned
&lt;/h2&gt;

&lt;p&gt;It helped me a lot to understand scaling of flex items by developing this tiny tool. I can encourage others to do the same for learning purposes and sharing knowledge.&lt;/p&gt;

&lt;p&gt;For me, a good understanding of &lt;code&gt;flex-grow&lt;/code&gt;, &lt;code&gt;flex-shrink&lt;/code&gt;, and &lt;code&gt;flex-basis&lt;/code&gt; in combination with known &lt;code&gt;width&lt;/code&gt;, &lt;code&gt;min-width&lt;/code&gt;, and &lt;code&gt;max-width&lt;/code&gt; properties allows for developing better (in terms of responsive and robust) flexbox layouts.&lt;/p&gt;

</description>
      <category>css</category>
      <category>flexbox</category>
      <category>scaling</category>
      <category>rwd</category>
    </item>
    <item>
      <title>Why Lerna and Yarn Workspaces is a Perfect Match for Building Mono-Repos: A Close Look at Features and Performance </title>
      <dc:creator>Sebastian Weber</dc:creator>
      <pubDate>Mon, 18 Mar 2019 22:32:56 +0000</pubDate>
      <link>https://forem.com/doppelmutzi/why-lerna-and-yarn-workspaces-is-a-perfect-match-for-building-mono-repos-a-close-look-at-features-and-performance--1me0</link>
      <guid>https://forem.com/doppelmutzi/why-lerna-and-yarn-workspaces-is-a-perfect-match-for-building-mono-repos-a-close-look-at-features-and-performance--1me0</guid>
      <description>&lt;p&gt;This post is my take on the topic of &lt;em&gt;Mono-Repo&lt;/em&gt;. After a brief introduction to &lt;em&gt;Mono-Repos&lt;/em&gt; and a comparison with &lt;em&gt;Multi-Repos&lt;/em&gt;, I go into tools for establishing &lt;em&gt;Mono-Repos&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;I don't want to assess in great detail what repository type is better in which circumstance. However, the goal of this article is all about &lt;em&gt;Mono-Repos&lt;/em&gt; and how &lt;a href="https://lernajs.io/" rel="noopener noreferrer"&gt;lerna&lt;/a&gt;, &lt;a href="https://www.npmjs.com/" rel="noopener noreferrer"&gt;npm&lt;/a&gt;, and &lt;a href="https://yarnpkg.com" rel="noopener noreferrer"&gt;yarn&lt;/a&gt; (&lt;a href="https://yarnpkg.com/en/package/workspaces" rel="noopener noreferrer"&gt;workspaces&lt;/a&gt;) can help. It also makes sense to use these tools in combination. Especially &lt;em&gt;lerna&lt;/em&gt; and &lt;em&gt;yarn workspaces&lt;/em&gt; can peacefully coexist in a project. How? We will find out in a minute.&lt;/p&gt;

&lt;h1&gt;
  
  
  What is a Mono-Repo? How does it Compare to Multi-Repo?
&lt;/h1&gt;

&lt;p&gt;Tools like &lt;em&gt;lerna&lt;/em&gt; and &lt;em&gt;yarn workspaces&lt;/em&gt; have been a decisive factor with the result that managing your codebase in a single repo (a.k.a. &lt;em&gt;Mono-Repo&lt;/em&gt;) has gained some traction for about one or two years. A lot of articles were written or conference talks were given about this topic.&lt;/p&gt;

&lt;p&gt;In short, a so-called &lt;em&gt;Mono-Repo&lt;/em&gt; is a (git) repository that houses multiple projects. Such projects are called workspaces or packages. In contrast, using multiple repositories with each repository housing only one project is called a &lt;em&gt;Multi-Repo&lt;/em&gt; approach.  Of course, a combination of both approaches is possible. In my current job, we constitute multiple teams where each team has its own repositories. There are teams that pursue a &lt;em&gt;Mono-Repo&lt;/em&gt; approach and there are teams believing in a &lt;em&gt;Multi-Repo&lt;/em&gt; maxim. Plus, there exists teams leveraging both approaches because the technology, which is part of the repository, is also a factor to have in mind for decision-making (e.g., every Java micro-service is part of an own git repo).&lt;/p&gt;

&lt;p&gt;To find out about differences along with pros and cons of &lt;em&gt;Mono-Repos&lt;/em&gt; and &lt;em&gt;Multi-Repos&lt;/em&gt;, I recommend Markus Oberlehner's article about &lt;a href="https://medium.com/@maoberlehner/monorepos-in-the-wild-33c6eb246cb9" rel="noopener noreferrer"&gt;Monorepos in the Wild&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Tool Landscape for Mono-Repos
&lt;/h1&gt;

&lt;p&gt;A &lt;em&gt;Mono-Repo&lt;/em&gt; hosts one or more projects or packages. These packages are "Mini-Repos" that can be versioned, built, and published independently. Therefore, every package contains its own &lt;em&gt;package.json&lt;/em&gt; file due to the fact that every package is a full-fledged project on its own.&lt;br&gt;
Packages might have dependency relations between each other. Managing these dependencies are implemented by symlinks.&lt;/p&gt;

&lt;p&gt;As we see later, &lt;em&gt;lerna&lt;/em&gt; and &lt;em&gt;yarn workspaces&lt;/em&gt; give us the ability to build libraries and apps in a single repo without forcing us to publish to npm or other registries. The beauty behind these technologies is that they can find package dependencies by analyzing &lt;em&gt;package.json&lt;/em&gt; files located at each project's root folder. Thereby, these tools make it obsolete to manually create symlinks or use "low-level" &lt;em&gt;npm link&lt;/em&gt; directly.&lt;/p&gt;

&lt;p&gt;This results in faster code-test-debug cycles by sharing components locally. &lt;em&gt;lerna&lt;/em&gt; and &lt;em&gt;yarn workspaces&lt;/em&gt; together improve the developer experience of managing multiple packages in a &lt;em&gt;Mono-Repo&lt;/em&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Correlation between &lt;em&gt;npm&lt;/em&gt;, &lt;em&gt;yarn&lt;/em&gt;, &lt;em&gt;yarn workspaces&lt;/em&gt;, and &lt;em&gt;lerna&lt;/em&gt;
&lt;/h2&gt;

&lt;p&gt;I want to shed some light on the clutter how &lt;em&gt;npm&lt;/em&gt;, &lt;em&gt;yarn&lt;/em&gt;, &lt;em&gt;yarn workspaces&lt;/em&gt;, and &lt;em&gt;lerna&lt;/em&gt; are involved in the topic of &lt;em&gt;Mono-repos&lt;/em&gt;. Take a look at the following "set diagram".&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F4em4gmf4sv1fn8snb0dj.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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F4em4gmf4sv1fn8snb0dj.png" alt="Native features"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It depicts three main players and how they correlate. By the way, do not take the proportions too seriously. The diagram's purpose is just to give an impression how things are connected.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;npm&lt;/em&gt; (marked by 1) and &lt;em&gt;yarn&lt;/em&gt; (2) are both native package managers that have many features in common (3). As an example, both leverage the concept of &lt;em&gt;package.json&lt;/em&gt; as container for dependency management, which was introduced by &lt;em&gt;npm&lt;/em&gt; back in the days. More shared concepts and features are dependency management, publishing, or using lock files to "freeze" dependency versions. There are even more features originated by &lt;em&gt;npm&lt;/em&gt; that are also leveraged by &lt;em&gt;yarn&lt;/em&gt;, such as publishing to npm registry.&lt;/p&gt;

&lt;p&gt;One of the reasons for creating &lt;em&gt;yarn&lt;/em&gt; in the first place was performance – it took too long to install dependencies in large projects with &lt;em&gt;npm&lt;/em&gt;. Another aspect was missing features, such as a sophisticated concept for freezing versions, offline capabilities, or deterministic behavior in terms of dependency resolution. Though, many of these gaps of &lt;em&gt;npm&lt;/em&gt; have vanished over the time and both technologies are more and more feature-compliant nowadays.&lt;/p&gt;

&lt;p&gt;Things that still belong solely to &lt;em&gt;npm&lt;/em&gt; (1) or &lt;em&gt;yarn&lt;/em&gt; (2) are &lt;em&gt;package-lock.json&lt;/em&gt; files or &lt;em&gt;yarn.lock&lt;/em&gt; files, respectively. However, for us, the application developers, the different implementation of lock files does not really matter. Practically, &lt;em&gt;npm&lt;/em&gt; and &lt;em&gt;yarn&lt;/em&gt; are even on how version management is handled.&lt;/p&gt;

&lt;p&gt;One big feature that is exclusive to &lt;em&gt;yarn&lt;/em&gt; is &lt;em&gt;yarn workspaces&lt;/em&gt; (4) that was added to &lt;em&gt;yarn&lt;/em&gt; about a year ago. It expands &lt;em&gt;yarn&lt;/em&gt; by native &lt;em&gt;Mono-Repo&lt;/em&gt; capabilities. The next section goes more into &lt;em&gt;Mono-Repo&lt;/em&gt; features.&lt;/p&gt;
&lt;h2&gt;
  
  
  Mono-Repo – What is native? What is user land?
&lt;/h2&gt;

&lt;p&gt;Consider the next diagram depicting how technologies in the Mono-Repo environment are connected to each other.&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fzlf96bhxo4whpu8i6f6p.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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fzlf96bhxo4whpu8i6f6p.png" alt="Mono-repo features"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Marked in red are technologies that provide Mono-Repo capabilities. All of them are based either on &lt;em&gt;npm&lt;/em&gt; or &lt;em&gt;yarn&lt;/em&gt;. The latter do not provide advanced features for building Mono-Repose besides &lt;em&gt;npm link&lt;/em&gt; or &lt;em&gt;yarn link&lt;/em&gt;, respectively.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://yarnpkg.com/lang/en/docs/workspaces/" rel="noopener noreferrer"&gt;yarn workspaces&lt;/a&gt; is the only representative that exposes Mono-Repo capabilities natively. &lt;a href="https://lernajs.io" rel="noopener noreferrer"&gt;lerna&lt;/a&gt; is around for quite some time and came out even before &lt;em&gt;yarn workspaces&lt;/em&gt; has existed. &lt;em&gt;lerna&lt;/em&gt; provides Mono-Repo features on the level of user land with the help of &lt;em&gt;npm&lt;/em&gt; or &lt;em&gt;yarn&lt;/em&gt; as dependency management tools.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;lerna&lt;/em&gt; leverages semantic links for this purpose. It also allows for using &lt;em&gt;yarn workspaces&lt;/em&gt; and, then, leaves the whole Mono-Repo aspect solely to the natively implemented features of &lt;em&gt;yarn workspaces&lt;/em&gt;. Furthermore, &lt;em&gt;lerna&lt;/em&gt; provides sophisticated publishing and version management features to even publish projects independently from each other. Short, &lt;em&gt;lerna&lt;/em&gt; offers many features beyond Mono-Repo management. On the other side, &lt;em&gt;yarn workspaces&lt;/em&gt; sole purpose is to ease the Mono-Repo workflow. So, you do not have to decide for either side of them. It totally does make sense to use &lt;em&gt;lerna&lt;/em&gt; with &lt;em&gt;yarn workspaces&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/boltpkg/bolt" rel="noopener noreferrer"&gt;bolt&lt;/a&gt; is a relatively new project that bases on &lt;em&gt;yarn workspaces&lt;/em&gt;. Inspired by &lt;em&gt;lerna&lt;/em&gt;, its goal is to add more helpful commands on this foundation. However, I do not have any experience with it since I haven't accomplished yet to get &lt;em&gt;bolt&lt;/em&gt; up and running in my playground project. In addition, I have realized that there have been relatively few commits lately. So, I do not go any deeper in this article.&lt;/p&gt;
&lt;h1&gt;
  
  
  Different Variants of Configuring &lt;em&gt;Mono-Repos&lt;/em&gt;
&lt;/h1&gt;

&lt;p&gt;This section's goal is to give a quick overview on how to set up the different tools in different variations. You can understand the screenshots as a kind of "cheat sheets". The focus is on the configuration part of the different approaches and how they differ.&lt;/p&gt;

&lt;p&gt;I created a small repository to demonstrate the different variants. Just &lt;a href="https://github.com/doppelmutzi/monorepo-playground" rel="noopener noreferrer"&gt;clone the demo project repo&lt;/a&gt; and switch branches for the different variants. The &lt;em&gt;README.md&lt;/em&gt; file describes how to bootstrap and use (i.e., build and run the dummy app) the particular variant. Another goal of this section and demo project is to provide an easy playground to see the different variants in action from different perspectives: which configuration steps are required, what steps are needed to build and use the sub projects (i.e., packages), how does dependency management work, or what are the timing implications for bootstrapping.&lt;/p&gt;
&lt;h2&gt;
  
  
  1. Do it yourself
&lt;/h2&gt;

&lt;p&gt;I skip this section but feel free to checkout branch &lt;em&gt;1-do-it-yourself&lt;/em&gt;. Basically you work with &lt;em&gt;npm link&lt;/em&gt; and have to create semantic links and install all sub projects manually. I hope you can imagine how tedious and impractical this scenario is for real-world projects.&lt;/p&gt;
&lt;h2&gt;
  
  
  2. lerna with npm
&lt;/h2&gt;

&lt;p&gt;To get support for automating such manual tasks of approach 1, &lt;em&gt;lerna&lt;/em&gt; was introduced. You need a &lt;em&gt;lerna.json&lt;/em&gt; file in the root folder. As a convention, &lt;em&gt;lerna&lt;/em&gt; uses &lt;em&gt;npm&lt;/em&gt; as default.&lt;/p&gt;

&lt;p&gt;As you see in the next screenshot, you basically need to edit two files for getting &lt;em&gt;lerna&lt;/em&gt; up and running: &lt;em&gt;lerna.json&lt;/em&gt; and &lt;em&gt;package.json&lt;/em&gt;. Within &lt;em&gt;lerna.json&lt;/em&gt; you need to specify where &lt;em&gt;lerna&lt;/em&gt; has to look for packages.&lt;/p&gt;

&lt;p&gt;To bootstrap all sub projects you need to execute &lt;em&gt;lerna bootstrap&lt;/em&gt; by invoking the following npm script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npm run bootstrap
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What this command basically does is to go into all packages' root folders and execute &lt;em&gt;npm install&lt;/em&gt;. Take a look at the three packages and you will see that &lt;em&gt;lerna&lt;/em&gt; caused npm to create a &lt;em&gt;node_modules&lt;/em&gt; folder for every package.&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fovmdxh3pn7l1yvutc3e0.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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fovmdxh3pn7l1yvutc3e0.jpg" alt="Setup lerna with npm"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  3. lerna with yarn
&lt;/h2&gt;

&lt;p&gt;This is the same setup as approach 2. The only difference is that you have to specify &lt;em&gt;yarn&lt;/em&gt; as client with the "npmClient" property in &lt;em&gt;lerna.json&lt;/em&gt; file.  Bootstrapping is also performed by &lt;em&gt;lerna&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;What is the difference in contrast to approach 1? Virtually nothing. Mainly it is a matter of taste because the only difference is whether &lt;em&gt;lerna&lt;/em&gt; utilizes &lt;em&gt;npm&lt;/em&gt; or &lt;em&gt;yarn&lt;/em&gt; as dependency manager. The answer to the question, which one to chose boils down to the following questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;which syntax do I prefer? &lt;em&gt;npm run &amp;lt;command&amp;gt;&lt;/em&gt; vs &lt;em&gt;yarn &amp;lt;command&amp;gt;&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Should I stick to the quasi-standard or do I like the effort of Facebook&lt;/li&gt;
&lt;li&gt;Do I really care about bootstrapping time? If so, take a look at the next chapter which provides some performance benchmarks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fwdpps0e2q43vuslaw4x9.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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fwdpps0e2q43vuslaw4x9.jpg" alt="Setup lerna with yarn"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  4. yarn workspaces
&lt;/h2&gt;

&lt;p&gt;For this approach, you do not require &lt;em&gt;lerna&lt;/em&gt;. &lt;em&gt;yarn workspaces&lt;/em&gt; come with built-in &lt;em&gt;Mono-Repo&lt;/em&gt; capabilities. To use &lt;em&gt;yarn workspaces&lt;/em&gt; you need &lt;em&gt;yarn&lt;/em&gt; version 1.0 or higher. As you can see in the following screenshot, you do not need a dedicated configuration file. The &lt;em&gt;package.json&lt;/em&gt; file in the root folder needs to be private and has to have a &lt;em&gt;"workspaces"&lt;/em&gt; property telling &lt;em&gt;yarn&lt;/em&gt; where to find the sub projects (or workspaces in &lt;em&gt;yarn&lt;/em&gt; speech).&lt;/p&gt;

&lt;p&gt;To bootstrap the project with all its workspaces, you just use &lt;em&gt;yarn&lt;/em&gt; since &lt;em&gt;yarn workspaces&lt;/em&gt; provides this feature natively:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;yarn &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or short:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;yarn
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This combines both steps of approach 1 and 2: Installing the dependencies of the root folder and bootstrapping of all packages' dependencies.&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fc78rsjpnt1zijj7m924f.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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fc78rsjpnt1zijj7m924f.jpg" alt="Setup yarn workspaces"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One big difference in comparison to approach 1 and 2 is that &lt;em&gt;yarn workspaces&lt;/em&gt; creates only one &lt;em&gt;node_modules&lt;/em&gt; folder. All dependencies are hoisted to the root folder. Remark: Meanwhile, this behavior is also possible with &lt;em&gt;lerna&lt;/em&gt; (without &lt;em&gt;yarn workspaces&lt;/em&gt;) by using the &lt;em&gt;--hoist&lt;/em&gt; flag.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. lerna with yarn workspaces
&lt;/h2&gt;

&lt;p&gt;To configure &lt;em&gt;lerna&lt;/em&gt; with &lt;em&gt;yarn workspaces&lt;/em&gt; you have to have the same configuration in the root's &lt;em&gt;package.json&lt;/em&gt; as described in approach 4. However, you need to provide a &lt;em&gt;lerna.json&lt;/em&gt; file in the root folder, too. There, you need to tell &lt;em&gt;lerna&lt;/em&gt; to use &lt;em&gt;yarn workspaces&lt;/em&gt;. Unfortunately, you have to specify the location of the sub projects redundantly in &lt;em&gt;lerna.json&lt;/em&gt;. To bootstrap the project, no &lt;em&gt;lerna bootstrap&lt;/em&gt; is required, you just have to use &lt;em&gt;yarn install&lt;/em&gt; as described in approach 4. It &lt;a href="https://github.com/lerna/lerna/issues/1308#issuecomment-370848535" rel="noopener noreferrer"&gt;doesn't make much sense&lt;/a&gt; to invoke &lt;em&gt;lerna bootstrap&lt;/em&gt; since it just calls &lt;em&gt;yarn install&lt;/em&gt; itself.&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fo4ryatl17uhe6r0f0i8o.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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fo4ryatl17uhe6r0f0i8o.jpg" alt="Setup lerna with yarn workspaces"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With this setup, &lt;em&gt;lerna&lt;/em&gt; completely dedicates the dependency and bootstrapping workflow to &lt;em&gt;yarn workspaces&lt;/em&gt;. So, you need to configure more to achieve the same as the previous approach. Why should you then use this way over approach 4? Well, think about this – using &lt;em&gt;lerna&lt;/em&gt; and &lt;em&gt;yarn workspaces&lt;/em&gt; at the same time makes totally sense. They coexist peacefully together in a &lt;em&gt;Mono-Repo&lt;/em&gt; project.&lt;/p&gt;

&lt;p&gt;In such a scenario:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You solely use &lt;em&gt;yarn workspaces&lt;/em&gt; for the &lt;em&gt;Mono-Repo&lt;/em&gt; workflow.&lt;/li&gt;
&lt;li&gt;You use &lt;em&gt;lerna&lt;/em&gt;'s utility commands to optimize managing of multiple packages, e.g., selective execution of &lt;em&gt;npm&lt;/em&gt; scripts for testing.&lt;/li&gt;
&lt;li&gt;You use &lt;em&gt;lerna&lt;/em&gt; for publishing packages since &lt;em&gt;lerna&lt;/em&gt; provides sophisticated features with its version and publish commands.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  lerna and yarn workspaces
&lt;/h1&gt;

&lt;p&gt;The last section gives a quick understanding on how to set up &lt;em&gt;Mono-Repos&lt;/em&gt; with different configurations. This section's focus is more on the features of &lt;em&gt;lerna&lt;/em&gt; and &lt;em&gt;yarn workspaces&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  yarn workspaces
&lt;/h2&gt;

&lt;p&gt;Up to date, &lt;em&gt;yarn workspaces&lt;/em&gt; constitutes the only technology that comes with native capabilities for &lt;em&gt;Mono-Repos&lt;/em&gt;. In contrast to &lt;em&gt;lerna&lt;/em&gt;, you do not have to execute a separate step for bootstrapping dependencies of the packages. &lt;em&gt;yarn install&lt;/em&gt; does the trick by installing the dependencies of the root folder and then for every package.&lt;/p&gt;

&lt;p&gt;In contrast to &lt;em&gt;lerna&lt;/em&gt;, &lt;em&gt;yarn workspaces&lt;/em&gt; does not come with additional features besides dependency management for multi-project setups. Since its foundation is &lt;em&gt;yarn&lt;/em&gt;, you have all of &lt;em&gt;yarn&lt;/em&gt;'s features on hand.&lt;/p&gt;

&lt;p&gt;For using &lt;em&gt;yarn workspaces&lt;/em&gt;, Facebook has introduced a few additional commands that do only make sense in the context of &lt;em&gt;Mono-Repos&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The following command will display the workspace dependency tree of your current project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;yarn workspaces info
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The next receipt enables you to run the chosen &lt;em&gt;yarn&lt;/em&gt; command in the selected workspace (i.e., package):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;yarn workspace &amp;lt;package-name&amp;gt; &amp;lt;&lt;span class="nb"&gt;command&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As an example, with the following command &lt;em&gt;react&lt;/em&gt; gets added to the package / workspace called "awesome-package" as dev dependency (instead of &lt;em&gt;--dev&lt;/em&gt; you can also use &lt;em&gt;-D&lt;/em&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;yarn workspace awesome-package add react &lt;span class="nt"&gt;--dev&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Up next is an example to remove a dependency from a particular package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;yarn workspace web-project remove some-package &lt;span class="nt"&gt;--save&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to add a common dependency to all packages, go into the project's root folder and use the &lt;em&gt;-W&lt;/em&gt; (or &lt;em&gt;--ignore-workspace-root-check&lt;/em&gt;) flag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;yarn add some-package &lt;span class="nt"&gt;-W&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Otherwise, you get an error by &lt;em&gt;yarn&lt;/em&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F6n1kjzlvw41w9bihrwfa.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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F6n1kjzlvw41w9bihrwfa.jpg" alt="Error without -W flag"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With the following command, I add one of my own packages ("awesome-components") to another package ("awesome-app") as dependency. I found out that adding local packages should be done by specifying a version number, otherwise &lt;em&gt;yarn&lt;/em&gt; tries to find the dependency in the registry.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;yarn workspace @doppelmutzi/awesome-app add @doppelmutzi/awesome-components@0.1.0 &lt;span class="nt"&gt;-D&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using the workspaces feature, &lt;em&gt;yarn&lt;/em&gt; does not add dependencies to &lt;em&gt;node_modules&lt;/em&gt; directories in either of your packages  –  only at the root level, i.e., &lt;em&gt;yarn&lt;/em&gt; hoists all dependencies to the root level. &lt;em&gt;yarn&lt;/em&gt; leverages symlinks to point to the different packages. Thereby, &lt;em&gt;yarn&lt;/em&gt; includes the dependencies only once in the project.&lt;/p&gt;

&lt;p&gt;You have to utilize &lt;em&gt;yarn workspaces&lt;/em&gt;' &lt;a href="https://yarnpkg.com/blog/2018/02/15/nohoist/" rel="noopener noreferrer"&gt;noHoist&lt;/a&gt; feature to use otherwise incompatible 3rd party dependencies working in the &lt;em&gt;Mono-Repo&lt;/em&gt; environment. You have to specify this in the project root &lt;em&gt;package.json&lt;/em&gt; as you can see in the following example.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;package.json&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"workspaces"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"packages"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"packages/*"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"nohoist"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"**/react-native"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For more information take a look at the &lt;a href="https://github.com/connectdotz/yarn-nohoist-examples" rel="noopener noreferrer"&gt;demo project&lt;/a&gt; of &lt;a href="https://github.com/connectdotz" rel="noopener noreferrer"&gt;ConnectDotz&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  lerna
&lt;/h2&gt;

&lt;p&gt;As with &lt;em&gt;yarn workspaces&lt;/em&gt;, &lt;em&gt;lerna&lt;/em&gt; adds &lt;em&gt;Mono-Rep&lt;/em&gt; capabilities to a frontend project. However, as described above, &lt;em&gt;lerna&lt;/em&gt; operates on "user land" and cannot add such functionality natively.&lt;/p&gt;

&lt;p&gt;If you configure &lt;em&gt;lerna&lt;/em&gt; to use &lt;em&gt;yarn workspaces&lt;/em&gt; then &lt;em&gt;lerna&lt;/em&gt; hands over the whole dependency management to &lt;em&gt;yarn workspaces&lt;/em&gt;. If you configure &lt;em&gt;lerna&lt;/em&gt; with &lt;em&gt;npm&lt;/em&gt; or &lt;em&gt;yarn&lt;/em&gt; then &lt;em&gt;lerna&lt;/em&gt; provides the &lt;em&gt;Mono-Repo&lt;/em&gt; capabilities on its own by utilizing symlinks. In such a context, you have to use &lt;em&gt;lerna bootstrap&lt;/em&gt; to initialize dependencies of all packages.&lt;/p&gt;

&lt;p&gt;John Tucker wrote a great article about using &lt;em&gt;lerna&lt;/em&gt;'s &lt;a href="https://codeburst.io/monorepos-by-example-part-1-3a883b49047e" rel="noopener noreferrer"&gt;commands to initialize projects and manage dependencies&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To install &lt;em&gt;react&lt;/em&gt; as dependency into all packages, you can use the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;lerna add react
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to install &lt;em&gt;react&lt;/em&gt; as dependency only to a particular package, execute the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;lerna add react &lt;span class="nt"&gt;--scope&lt;/span&gt; my-package
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you have installed &lt;em&gt;react&lt;/em&gt; for every package but would like to upgrade/downgrade to a particular version only for a specific package then you can do it like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;lerna add react@16.0.0 &lt;span class="nt"&gt;--scope&lt;/span&gt; my-package
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;lerna&lt;/em&gt; comes with a &lt;a href="https://github.com/lerna/lerna/tree/master/core/filter-options" rel="noopener noreferrer"&gt;couple of flags&lt;/a&gt;. They constitute options for &lt;em&gt;lerna&lt;/em&gt;'s sub-commands that need filtering.&lt;/p&gt;

&lt;p&gt;Consider the following npm script called "test". The two shell commands below show how to execute testing only on particular packages by using the &lt;em&gt;--scope&lt;/em&gt; flag along with globs. &lt;em&gt;lerna&lt;/em&gt; tries to execute &lt;em&gt;yarn test&lt;/em&gt; for every package that matches.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;package.json&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lerna exec yarn test“
  }
  ...
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;yarn &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;--scope&lt;/span&gt; @my-company-services/&lt;span class="k"&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;yarn &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;--scope&lt;/span&gt; @my-company/web-&lt;span class="k"&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;According to the &lt;a href="https://github.com/lerna/lerna/blob/master/doc/hoist.md" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;, &lt;em&gt;lerna&lt;/em&gt; also provides hoisting shared dependencies up to the root folder just like &lt;em&gt;yarn workspaces&lt;/em&gt;' default behavior. Therefore, you have to use the &lt;em&gt;--hoist&lt;/em&gt; flag.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;lerna add react &lt;span class="nt"&gt;-D&lt;/span&gt; &lt;span class="nt"&gt;--hoist&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you use &lt;em&gt;lerna&lt;/em&gt;, a question is whether to choose &lt;em&gt;npm&lt;/em&gt; or &lt;em&gt;yarn&lt;/em&gt;. As you can see with the "cheat sheets" in the last section, you can switch easily between the different package managers just as you like.&lt;/p&gt;

&lt;h2&gt;
  
  
  Advanced Frontend Workflow Features and Commands
&lt;/h2&gt;

&lt;p&gt;Even if you pick &lt;em&gt;yarn workspaces&lt;/em&gt; for dependency management, it is a good idea to use &lt;em&gt;lerna&lt;/em&gt; as well. The reason is that &lt;em&gt;lerna&lt;/em&gt; provides &lt;a href="https://github.com/lerna/lerna/tree/master/commands" rel="noopener noreferrer"&gt;utility commands&lt;/a&gt; to optimize management of multiple packages. For example, with one &lt;em&gt;lerna&lt;/em&gt; command you can iterate through all or particular packages, running a series of operations (such as linting, testing, and building) on each package. Thereby, it compliments &lt;em&gt;yarn workspaces&lt;/em&gt; that takes over the dependency management process.&lt;/p&gt;

&lt;p&gt;Using &lt;em&gt;lerna&lt;/em&gt; for testing or linting from within the root folder is faster than invoking all operations manually from every package folder. John Tucker's blog post deals with &lt;a href="https://codeburst.io/monorepos-by-example-part-2-4153712cfa31" rel="noopener noreferrer"&gt;testing with &lt;em&gt;lerna&lt;/em&gt;&lt;/a&gt; in great detail.&lt;/p&gt;

&lt;p&gt;Versioning and publishing are important development topics where &lt;em&gt;lerna&lt;/em&gt; also shines. &lt;em&gt;lerna&lt;/em&gt; allows you to use two versioning modes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;fixed/locked mode&lt;/em&gt;: The version of each package can be managed at a single point (in &lt;em&gt;lerna.json&lt;/em&gt; file). If a package has been updated since the last time a release was made, it will be updated to the new version. As a consequence, a major change in any package will result in all packages having a new major version.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;independent mode&lt;/em&gt;: Package versions can be incremented independently of each other. Therefore, the "version" key inside of &lt;em&gt;lerna.json&lt;/em&gt; needs to be set to "independent". This approach provides much more flexibility and is especially useful for projects with loosely coupled components.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can publish packages that have changed since the last release:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;lerna publish
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In independent mode, there exist different options to affect version bumping with the publish command. In addition to use a &lt;em&gt;semver&lt;/em&gt; keyword, you can also utilize one of the following version bump flags: &lt;a href="https://github.com/lerna/lerna/tree/master/commands/publish#bump-from-git" rel="noopener noreferrer"&gt;from-git&lt;/a&gt; or &lt;a href="https://github.com/lerna/lerna/tree/master/commands/publish#bump-from-package" rel="noopener noreferrer"&gt;from-package&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The following command publishes to a npm registry while using the &lt;a href="https://www.conventionalcommits.org/en/v1.0.0-beta.2/" rel="noopener noreferrer"&gt;conventional commits standard&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;lerna publish &lt;span class="nt"&gt;--conventional-commits&lt;/span&gt; &lt;span class="nt"&gt;--yes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above command also generates change log files. According to &lt;a href="https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-angular#angular-convention" rel="noopener noreferrer"&gt;lerna's docu&lt;/a&gt;, there exist different change log presets, such as &lt;em&gt;angular&lt;/em&gt; or &lt;em&gt;bitbucket&lt;/em&gt;. By the way, the &lt;a href="https://github.com/lerna/lerna/tree/master/commands/version#--yes" rel="noopener noreferrer"&gt;yes flag&lt;/a&gt; skips all confirmation prompts.&lt;/p&gt;

&lt;p&gt;Within &lt;em&gt;lerna.json&lt;/em&gt;, you can globally define that conventional commits have to be used without using flags:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;lerna.json&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"publish"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="nl"&gt;"conventionalCommits"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="nl"&gt;"yes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://dev.tojsilvax"&gt;@jsilvax&lt;/a&gt; explains &lt;a href="https://medium.com/@jsilvax/a-workflow-guide-for-lerna-with-yarn-workspaces-60f97481149d" rel="noopener noreferrer"&gt;how conventional commits with &lt;em&gt;lerna&lt;/em&gt; works&lt;/a&gt; and how it can be enforced with &lt;em&gt;commitlint&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Since versioning and publishing are complex topics, the section above shows only a small sample of &lt;em&gt;lerna&lt;/em&gt;'s possibilities. I do not go more into detail because this would go beyond the scope of this article.&lt;/p&gt;

&lt;h1&gt;
  
  
  Timing Comparison
&lt;/h1&gt;

&lt;p&gt;One of the main reasons why folks stick to &lt;em&gt;yarn&lt;/em&gt; instead of &lt;em&gt;npm&lt;/em&gt; is performance in terms of time for installing dependencies. Originally, &lt;em&gt;yarn&lt;/em&gt; was developed due to the fact that &lt;em&gt;npm&lt;/em&gt; took way too long for installing dependencies (besides the fact that &lt;em&gt;npm&lt;/em&gt; had lacked some important features). Meanwhile, &lt;em&gt;npm&lt;/em&gt; is available in version 6 and has put a lot of effort to eliminate this gap.&lt;/p&gt;

&lt;p&gt;Because you can achieve a &lt;em&gt;Mono-Repo&lt;/em&gt; in a variety of ways, let's take a look how these different approaches perform. In the remainder of this section, I present the results of my performance experiment. I cloned &lt;a href="https://github.com/babel/babel" rel="noopener noreferrer"&gt;the Babel project&lt;/a&gt; (approximately in October 2018) because it represents a real-life &lt;em&gt;Mono-Repo&lt;/em&gt; with many packages (142 to be precise). Interestingly, the original setup of &lt;em&gt;Babel&lt;/em&gt; utilizes &lt;em&gt;lerna&lt;/em&gt; with a config that specifies &lt;em&gt;yarn&lt;/em&gt; as &lt;em&gt;npmClient&lt;/em&gt; (no &lt;em&gt;yarn workspaces&lt;/em&gt;) and deactivates &lt;em&gt;yarn&lt;/em&gt;'s lock file generation.&lt;/p&gt;

&lt;p&gt;For every approach (2 – 5) I did the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I changed the configuration required for the corresponding approach (i.e., adapt &lt;em&gt;package.json&lt;/em&gt; and &lt;em&gt;lerna.json&lt;/em&gt; if required).&lt;/li&gt;
&lt;li&gt;I measured the elapsed time for installation of dependencies and for a dedicated bootstrapping step (if required).&lt;/li&gt;
&lt;li&gt;I measured the time for 3 different use cases. For every use case I performed measurements for 3 times.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The aforementioned use cases (UC) are:&lt;/p&gt;

&lt;p&gt;1) I empty npm or yarn cache, I remove all &lt;em&gt;node_modules&lt;/em&gt; folders, and I remove all &lt;em&gt;package-lock.json&lt;/em&gt; or &lt;em&gt;yarn.lock&lt;/em&gt; files.&lt;br&gt;
2) Cache exists, I remove all &lt;em&gt;node_modules&lt;/em&gt; folders, and I remove all &lt;em&gt;package-lock.json&lt;/em&gt; or &lt;em&gt;yarn.lock&lt;/em&gt; files.&lt;br&gt;
3) Cache exists, &lt;em&gt;package-lock.json&lt;/em&gt; or &lt;em&gt;yarn.lock&lt;/em&gt; files exist, I remove all &lt;em&gt;node_modules&lt;/em&gt; folders.&lt;/p&gt;

&lt;p&gt;For purging the cache, I executed one of the following commands depending on the used &lt;em&gt;npm&lt;/em&gt; client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npm cache clean &lt;span class="nt"&gt;--force&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;yarn cache clean
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As a helper for removing lock files and &lt;em&gt;node_modules&lt;/em&gt; folders, I added a script to Babel's root folder called &lt;em&gt;cleanup.sh&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;find &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s1"&gt;'yarn.lock'&lt;/span&gt; &lt;span class="nt"&gt;-exec&lt;/span&gt; &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt; +
find &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s1"&gt;'package-lock.json'&lt;/span&gt; &lt;span class="nt"&gt;-exec&lt;/span&gt; &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt; +
find &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"node_modules"&lt;/span&gt; &lt;span class="nt"&gt;-type&lt;/span&gt; d &lt;span class="nt"&gt;-prune&lt;/span&gt; &lt;span class="nt"&gt;-exec&lt;/span&gt; &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; &lt;span class="s1"&gt;'{}'&lt;/span&gt; +
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Depending on the use case, I eventually commented out the first 2 lines.&lt;/p&gt;

&lt;p&gt;For measuring the execution time of the steps for installing and bootstrapping dependencies, I utilized &lt;a href="https://www.npmjs.com/package/gnomon" rel="noopener noreferrer"&gt;gnomon&lt;/a&gt;. The following command constitutes an example for approach 2 (&lt;em&gt;lerna&lt;/em&gt; with &lt;em&gt;npm&lt;/em&gt;) and UC 1 (empty cache, no &lt;em&gt;node_modules&lt;/em&gt; folders, no lock files as precondition) for how I measured elapsed time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npm cache clean &lt;span class="nt"&gt;--force&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; ./cleanup.sh &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm i | gnomon &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm run bootstrap | gnomon
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Below, you will find the different measurements. I performed these measurements over time, so I have played around with different &lt;em&gt;node&lt;/em&gt;, &lt;em&gt;npm&lt;/em&gt;, &lt;em&gt;yarn&lt;/em&gt;, and &lt;em&gt;lerna&lt;/em&gt; versions to find out if different versions have different performance implications.&lt;/p&gt;

&lt;p&gt;To switch &lt;em&gt;node&lt;/em&gt; and &lt;em&gt;npm&lt;/em&gt; versions, I utilized &lt;a href="https://github.com/creationix/nvm" rel="noopener noreferrer"&gt;nvm&lt;/a&gt;. The following example first installs and uses &lt;em&gt;v9&lt;/em&gt; of &lt;em&gt;node&lt;/em&gt; and then installs &lt;em&gt;v5.7.1&lt;/em&gt; of &lt;em&gt;npm&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;nvm &lt;span class="nb"&gt;install &lt;/span&gt;v9
&lt;span class="nv"&gt;$ &lt;/span&gt;nvm use v9
&lt;span class="nv"&gt;$ &lt;/span&gt;npm i &lt;span class="nt"&gt;-g&lt;/span&gt; npm@5.7.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Approach 2 (lerna with npm) – Node v10.12.0 / npm v6.4.1 / lerna 2.11.0
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;UC&lt;/th&gt;
&lt;th&gt;Install&lt;/th&gt;
&lt;th&gt;Bootstrap&lt;/th&gt;
&lt;th&gt;Overall&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;39.1680s&lt;/td&gt;
&lt;td&gt;64.7168s&lt;/td&gt;
&lt;td&gt;103.8848s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;40.8052s&lt;/td&gt;
&lt;td&gt;78.0730s&lt;/td&gt;
&lt;td&gt;118.8782s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;39.8729s&lt;/td&gt;
&lt;td&gt;64.0626s&lt;/td&gt;
&lt;td&gt;103.9355s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;23.9931s&lt;/td&gt;
&lt;td&gt;34.8695s&lt;/td&gt;
&lt;td&gt;58.8626s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;23.8788s&lt;/td&gt;
&lt;td&gt;38.7979s&lt;/td&gt;
&lt;td&gt;62.6767s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;25.4764s&lt;/td&gt;
&lt;td&gt;37.5166s&lt;/td&gt;
&lt;td&gt;62.993s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;16.7291s&lt;/td&gt;
&lt;td&gt;35.8081s&lt;/td&gt;
&lt;td&gt;52.5372s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;29.4270s&lt;/td&gt;
&lt;td&gt;72.3721s&lt;/td&gt;
&lt;td&gt;101.7991s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;39.4265s&lt;/td&gt;
&lt;td&gt;85.0043s&lt;/td&gt;
&lt;td&gt;124.4308s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Remark: To be honest, I do not know why the deviations of the last two entries are so high – maybe my Macbook's workload were too high?!&lt;/p&gt;

&lt;h2&gt;
  
  
  Approach 2 (lerna with npm) – Node v9.10.0 / npm v5.6.0 / lerna 2.11.0
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;UC&lt;/th&gt;
&lt;th&gt;Install&lt;/th&gt;
&lt;th&gt;Bootstrap&lt;/th&gt;
&lt;th&gt;Overall&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;38.1641s&lt;/td&gt;
&lt;td&gt;52.7642s&lt;/td&gt;
&lt;td&gt;90.9283s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;33.3413s&lt;/td&gt;
&lt;td&gt;57.4676s&lt;/td&gt;
&lt;td&gt;90.8089s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;32.3160s&lt;/td&gt;
&lt;td&gt;52.4869s&lt;/td&gt;
&lt;td&gt;84.8029s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;24.3268s&lt;/td&gt;
&lt;td&gt;41.6709s&lt;/td&gt;
&lt;td&gt;65.9977s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;26.4843s&lt;/td&gt;
&lt;td&gt;41.6038s&lt;/td&gt;
&lt;td&gt;68.0881s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;29.8368s&lt;/td&gt;
&lt;td&gt;43.3759s&lt;/td&gt;
&lt;td&gt;73.2127s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;18.2647s&lt;/td&gt;
&lt;td&gt;33.7095s&lt;/td&gt;
&lt;td&gt;51.9742s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;15.2864s&lt;/td&gt;
&lt;td&gt;33.4166s&lt;/td&gt;
&lt;td&gt;48.7030s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;15.9295s&lt;/td&gt;
&lt;td&gt;34.6834s&lt;/td&gt;
&lt;td&gt;50.6129s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Approach 3 (lerna with yarn) – Node v10.12.0 / yarn 1.10.1 / lerna 2.11.0
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;UC&lt;/th&gt;
&lt;th&gt;Install&lt;/th&gt;
&lt;th&gt;Bootstrap&lt;/th&gt;
&lt;th&gt;Overall&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;36.5181s&lt;/td&gt;
&lt;td&gt;58.5693s&lt;/td&gt;
&lt;td&gt;95.0874s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;29.9026s&lt;/td&gt;
&lt;td&gt;53.8042s&lt;/td&gt;
&lt;td&gt;83.7068s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;30.8910s&lt;/td&gt;
&lt;td&gt;60.2566s&lt;/td&gt;
&lt;td&gt;91.1476s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;15.6954s&lt;/td&gt;
&lt;td&gt;34.9247s&lt;/td&gt;
&lt;td&gt;50.6201s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;24.4038s&lt;/td&gt;
&lt;td&gt;36.8669s&lt;/td&gt;
&lt;td&gt;61.2707s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;16.1917s&lt;/td&gt;
&lt;td&gt;36.4996s&lt;/td&gt;
&lt;td&gt;52.6913s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;9.2134s&lt;/td&gt;
&lt;td&gt;29.0799s&lt;/td&gt;
&lt;td&gt;38.2933s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;10.1278s&lt;/td&gt;
&lt;td&gt;27.1641s&lt;/td&gt;
&lt;td&gt;37.2919s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;10.2387s&lt;/td&gt;
&lt;td&gt;28.1842s&lt;/td&gt;
&lt;td&gt;38.4229s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Approach 3 (lerna with yarn) – Node v9.10.0 / yarn 1.10.1 / lerna 2.11.0
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;UC&lt;/th&gt;
&lt;th&gt;Install&lt;/th&gt;
&lt;th&gt;Bootstrap&lt;/th&gt;
&lt;th&gt;Overall&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;52.3567s&lt;/td&gt;
&lt;td&gt;69.5431s&lt;/td&gt;
&lt;td&gt;121.8998s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;45.3363s&lt;/td&gt;
&lt;td&gt;56.1238s&lt;/td&gt;
&lt;td&gt;101.4601s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;40.0621s&lt;/td&gt;
&lt;td&gt;54.2408s&lt;/td&gt;
&lt;td&gt;94.3029s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;23.2312s&lt;/td&gt;
&lt;td&gt;40.1567s&lt;/td&gt;
&lt;td&gt;63.3879s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;22.7905s&lt;/td&gt;
&lt;td&gt;39.2331s&lt;/td&gt;
&lt;td&gt;62.0236s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;21.3754s&lt;/td&gt;
&lt;td&gt;37.9659s&lt;/td&gt;
&lt;td&gt;59.3413s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;13.4165s&lt;/td&gt;
&lt;td&gt;28.6476s&lt;/td&gt;
&lt;td&gt;42.0641s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;13.2283s&lt;/td&gt;
&lt;td&gt;27.9781s&lt;/td&gt;
&lt;td&gt;41.2064s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;12.6465s&lt;/td&gt;
&lt;td&gt;29.3560s&lt;/td&gt;
&lt;td&gt;42.0025s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Approach 4 (yarn workspaces) – Node v10.12.0 / yarn 1.10.1
&lt;/h2&gt;

&lt;p&gt;There is no "bootstrap" step required because &lt;em&gt;yarn install&lt;/em&gt; does this under the hood.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;UC&lt;/th&gt;
&lt;th&gt;Install&lt;/th&gt;
&lt;th&gt;Bootstrap&lt;/th&gt;
&lt;th&gt;Overall&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;34.9199s&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;34.9199s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;31.8336s&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;31.8336s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;32.6647s&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;32.6647s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;17.9583s&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;17.9583s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;17.7032s&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;17.7032s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;17.9703s&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;17.9703s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;12.6103s&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;12.6103s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;13.4137s&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;13.4137s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;12.8213s&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;12.8213s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Approach 4 (yarn workspaces) – Node v11.2.0 / yarn 1.10.1
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;UC&lt;/th&gt;
&lt;th&gt;Install&lt;/th&gt;
&lt;th&gt;Bootstrap&lt;/th&gt;
&lt;th&gt;Overall&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;65.1631s&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;65.1631s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;69.0633s&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;69.0633s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;63.1915s&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;63.1915s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;25.6090s&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;25.6090s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;22.4050s&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;22.4050s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;24.7715s&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;24.7715s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;18.0540s&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;18.0540s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;18.8891s&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;18.8891s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;17.0438s&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;17.0438s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Approach 5 (lena with yarn workspaces) – Node v11.6.0 (npm v6.5.0-next.0) / yarn 1.12.3 / lerna 3.8.0
&lt;/h2&gt;

&lt;p&gt;With this approach, I try to find out if using &lt;em&gt;yarn workspaces&lt;/em&gt; as part of a &lt;em&gt;lerna&lt;/em&gt; configuration makes any difference regarding approach 4. Because there is no &lt;em&gt;lerna bootstrap&lt;/em&gt; required, the corresponding column is empty.&lt;/p&gt;

&lt;p&gt;But as I have expected, there is no difference to approach 4 since &lt;em&gt;lerna&lt;/em&gt; is not involved in the dependency installation / bootstrapping process.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;UC&lt;/th&gt;
&lt;th&gt;Install&lt;/th&gt;
&lt;th&gt;Bootstrap&lt;/th&gt;
&lt;th&gt;Overall&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;60.4779s&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;60.4779s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;63.3936s&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;63.3936s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;58.1888s&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;58.1888s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;32.7976s&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;32.7976s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;30.8835s&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;30.8835s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;28.9111s&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;28.9111s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;16.4637s&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;16.4637s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;17.8068s&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;17.8068s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;16.3400s&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;16.3400s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Approach 6 (lerna + npm ci + Audit) – Node v10.12.0 / npm v6.4.1 / lerna 3.4.3
&lt;/h2&gt;

&lt;p&gt;In this approach I use &lt;em&gt;lerna&lt;/em&gt; with &lt;a href="https://blog.npmjs.org/post/171556855892/introducing-npm-ci-for-faster-more-reliable" rel="noopener noreferrer"&gt;npm ci&lt;/a&gt; that constitutes an alternative for &lt;em&gt;npm install&lt;/em&gt; in a continuous integration context. Starting with version 3 of &lt;em&gt;lerna&lt;/em&gt;, &lt;em&gt;npm ci&lt;/em&gt; is the default for the installation command. However, you can opt-out.&lt;/p&gt;

&lt;p&gt;For this approach, &lt;em&gt;package-lock.json&lt;/em&gt; files have to exist. &lt;em&gt;node_modules&lt;/em&gt; folders should have been deleted, otherwise you get warnings printed out to the terminal. Thus, UC 3 is not possible.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;UC&lt;/th&gt;
&lt;th&gt;Install&lt;/th&gt;
&lt;th&gt;Bootstrap&lt;/th&gt;
&lt;th&gt;Overall&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;7.9733s&lt;/td&gt;
&lt;td&gt;34.1282s&lt;/td&gt;
&lt;td&gt;42.1015s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;9.3572s&lt;/td&gt;
&lt;td&gt;35.0904s&lt;/td&gt;
&lt;td&gt;44.4476s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;8.9436s&lt;/td&gt;
&lt;td&gt;36.3684s&lt;/td&gt;
&lt;td&gt;45.31200s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;10.8888s&lt;/td&gt;
&lt;td&gt;49.3526s&lt;/td&gt;
&lt;td&gt;60.2414s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;10.9077s&lt;/td&gt;
&lt;td&gt;44.9243s&lt;/td&gt;
&lt;td&gt;55.8320s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;11.5785s&lt;/td&gt;
&lt;td&gt;43.6369s&lt;/td&gt;
&lt;td&gt;55.2154s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Aproach 6 (lerna + npm ci) – Node v9 / npm v5.7.1 / lerna 3.4.3
&lt;/h2&gt;

&lt;p&gt;Using this exact &lt;em&gt;npm&lt;/em&gt; version, the &lt;em&gt;npm ci&lt;/em&gt; command is available but without the &lt;a href="https://blog.npmjs.org/post/173719309445/npm-audit-identify-and-fix-insecure" rel="noopener noreferrer"&gt;auditing feature&lt;/a&gt;. I wanted to test this setup to find out if there are any performance implications without auditing the dependencies. Again, UC 3 is not possible in this scenario.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;UC&lt;/th&gt;
&lt;th&gt;Install&lt;/th&gt;
&lt;th&gt;Bootstrap&lt;/th&gt;
&lt;th&gt;Overall&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;9.0732s&lt;/td&gt;
&lt;td&gt;29.8326s&lt;/td&gt;
&lt;td&gt;38.9058s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;9.3738s&lt;/td&gt;
&lt;td&gt;30.0418s&lt;/td&gt;
&lt;td&gt;39.4156s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;8.8552s&lt;/td&gt;
&lt;td&gt;29.1426s&lt;/td&gt;
&lt;td&gt;37.9978s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;11.7469s&lt;/td&gt;
&lt;td&gt;39.9573s&lt;/td&gt;
&lt;td&gt;51.7042s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;13.3401s&lt;/td&gt;
&lt;td&gt;44.6026s&lt;/td&gt;
&lt;td&gt;57.9427s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;13.3603s&lt;/td&gt;
&lt;td&gt;39.9416s&lt;/td&gt;
&lt;td&gt;53.3019s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;Based on my measurements, I do not see any notable differences between &lt;em&gt;npm&lt;/em&gt; and &lt;em&gt;yarn (workspaces)&lt;/em&gt; in terms of performance. From a feature point of view, both do not distinguish either. For me, it's a matter of taste which package manager to utilize. Furthermore, they can be swapped anytime or used in conjunction.&lt;/p&gt;

&lt;p&gt;Currently, I prefer to use &lt;em&gt;yarn workspaces&lt;/em&gt; as &lt;em&gt;Mono-Repo&lt;/em&gt; technology because I like its hoisting capabilities. On the other side, this is also possible with &lt;em&gt;lerna&lt;/em&gt; and its &lt;em&gt;--hoist&lt;/em&gt; flag. In my opinion, &lt;em&gt;yarn workspaces&lt;/em&gt; with &lt;em&gt;lerna&lt;/em&gt; is a good match: Configure &lt;em&gt;lerna&lt;/em&gt; to leave dependency management to &lt;em&gt;yarn workspaces&lt;/em&gt; and use its utility commands instead.&lt;/p&gt;

&lt;p&gt;Originally published at &lt;a href="https://doppelmutzi.github.io/monorepo-lerna-yarn-workspaces/" rel="noopener noreferrer"&gt;doppelmutzi.github.io&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>monorepo</category>
      <category>lerna</category>
      <category>yarnworkspaces</category>
      <category>performance</category>
    </item>
  </channel>
</rss>
