<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Bartosz Gordon</title>
    <description>The latest articles on DEV Community by Bartosz Gordon (@bgord).</description>
    <link>https://dev.to/bgord</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%2F119094%2Fddc9b9bc-21a2-459c-a4e3-ad40c32dc1f6.png</url>
      <title>DEV Community: Bartosz Gordon</title>
      <link>https://dev.to/bgord</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/bgord"/>
    <language>en</language>
    <item>
      <title>Configure nginx for Gatsby 404 error page</title>
      <dc:creator>Bartosz Gordon</dc:creator>
      <pubDate>Tue, 08 Jun 2021 21:05:05 +0000</pubDate>
      <link>https://dev.to/bgord/configure-nginx-for-gatsby-404-error-page-5ecf</link>
      <guid>https://dev.to/bgord/configure-nginx-for-gatsby-404-error-page-5ecf</guid>
      <description>&lt;p&gt;During Gatsby page development, when you hit a page that doesn't exist, you encounter a screen similar to the one below.&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%2Fbartoszgordon.com%2Fgatsby-404-nginx-development.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%2Fbartoszgordon.com%2Fgatsby-404-nginx-development.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you created a custom 404 page, for example in &lt;code&gt;src/pages/404.js&lt;/code&gt;, you can click the &lt;code&gt;Preview custom 404 page&lt;/code&gt; to display it. Entering the &lt;code&gt;/404&lt;/code&gt; route will do the job as well.&lt;/p&gt;




&lt;p&gt;In production, however, the 404 page doesn't get handled by itself unless you use a dedicated hosting service for Gatsby pages. That's because Gatsby's development server is no longer present. The production bundle is just a bunch of static files. Making use of a web server (like &lt;code&gt;nginx&lt;/code&gt;) to serve the production page forces you to configure the error pages yourself.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Disclaimer: the following &lt;code&gt;nginx&lt;/code&gt; configuration is pretty basic, don't use it in production.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let's assume your blog is hosted on the &lt;code&gt;personalblog.com&lt;/code&gt; domain and is served from the &lt;code&gt;/var/www/blog&lt;/code&gt; directory on port &lt;code&gt;80&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;root&lt;/span&gt; &lt;span class="n"&gt;/var/www/blog&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;personalblog.com&lt;/span&gt; &lt;span class="s"&gt;www.personalblog.com&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="kn"&gt;try_files&lt;/span&gt; &lt;span class="nv"&gt;$uri&lt;/span&gt; &lt;span class="nv"&gt;$uri&lt;/span&gt;&lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After entering a route that doesn't exist in your production Gatsby site, &lt;code&gt;nginx&lt;/code&gt; displays its default 404 error page.&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%2Fbartoszgordon.com%2Fgatsby-404-nginx-production.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%2Fbartoszgordon.com%2Fgatsby-404-nginx-production.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Adding the &lt;code&gt;error_page 404 /404;&lt;/code&gt; line makes &lt;code&gt;nginx&lt;/code&gt; redirect to the &lt;code&gt;/404&lt;/code&gt; route in case of a 404 error. Your custom Gatsby 404 page will be displayed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;root&lt;/span&gt; &lt;span class="n"&gt;/var/www/blog&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;personalblog.com&lt;/span&gt; &lt;span class="s"&gt;www.personalblog.com&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;error_page&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt; &lt;span class="n"&gt;/404&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="kn"&gt;try_files&lt;/span&gt; &lt;span class="nv"&gt;$uri&lt;/span&gt; &lt;span class="nv"&gt;$uri&lt;/span&gt;&lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Visit the &lt;a href="https://nginx.org/en/docs/http/ngx_http_core_module.html#error_page" rel="noopener noreferrer"&gt;error_page&lt;/a&gt; docs section to explore the rest of the configuration options.&lt;/p&gt;

</description>
      <category>nginx</category>
      <category>gatsby</category>
      <category>react</category>
      <category>devops</category>
    </item>
    <item>
      <title>One-liner to sum up numbers from a CSV file</title>
      <dc:creator>Bartosz Gordon</dc:creator>
      <pubDate>Mon, 07 Jun 2021 20:18:03 +0000</pubDate>
      <link>https://dev.to/bgord/one-liner-to-sum-up-numbers-from-a-csv-file-1g2g</link>
      <guid>https://dev.to/bgord/one-liner-to-sum-up-numbers-from-a-csv-file-1g2g</guid>
      <description>&lt;p&gt;This is the &lt;code&gt;shopping.csv&lt;/code&gt; CSV file that we will be working with throughout this blog post.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;item,price
rice,2.49
potatos,1.49
pasta,3.79
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The goal is to create a one-line bash script that prints the sum of the prices in the file above, which is &lt;strong&gt;7.77&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step: 1. Print the file content&lt;/strong&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;&lt;span class="nb"&gt;cat &lt;/span&gt;shopping.csv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command prints the file content without modifications.&lt;br&gt;
It gives us a way to redirect - pipe - the output to another command in the next step.&lt;/p&gt;

&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;item,price
rice,2.49
potatos,1.49
pasta,3.79
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step: 2. Cut the first line&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;There are many ways to achieve it, but we use the &lt;code&gt;tail +n&lt;/code&gt; syntax.&lt;/p&gt;

&lt;p&gt;In our case, it takes all the lines until the end of the file, starting from the second line.&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;&lt;span class="nb"&gt;cat &lt;/span&gt;shopping.csv | &lt;span class="nb"&gt;tail&lt;/span&gt; +2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rice,2.49
potatos,1.49
pasta,3.79
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step: 3. Cut the first column&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;awk&lt;/code&gt; splits each line by a separator defined by the option &lt;code&gt;-F&lt;/code&gt;.&lt;br&gt;
In our case, it's the comma.&lt;br&gt;
Then it prints the second column from every row.&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;&lt;span class="nb"&gt;cat &lt;/span&gt;shopping.csv | &lt;span class="nb"&gt;tail&lt;/span&gt; +2 | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt; , &lt;span class="s1"&gt;'{print $2}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2.49
1.49
3.79
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step: 4. Concatenate the prices&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;xargs&lt;/code&gt; is used to "squash" the lines into a single string, separating them by space.&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;&lt;span class="nb"&gt;cat &lt;/span&gt;shopping.csv | &lt;span class="nb"&gt;tail&lt;/span&gt; +2 | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt; , &lt;span class="s1"&gt;'{print $2}'&lt;/span&gt; | xargs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2.49 1.49 3.79
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step: 5. Replace spaces with pluses&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;sed&lt;/code&gt; expression replaces the escaped space with &lt;code&gt;+&lt;/code&gt; in the entire string (globally - &lt;code&gt;g&lt;/code&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;&lt;span class="nb"&gt;cat &lt;/span&gt;shopping.csv | &lt;span class="nb"&gt;tail&lt;/span&gt; +2 | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt; , &lt;span class="s1"&gt;'{print $2}'&lt;/span&gt; | xargs | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'s/\ /+/g'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2.49+1.49+3.79
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step: 6. Perform the calculation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;bc&lt;/code&gt; is a simple calculator that you can use interactively or by piping an equation into it.&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;&lt;span class="nb"&gt;cat &lt;/span&gt;shopping.csv | &lt;span class="nb"&gt;tail&lt;/span&gt; +2 | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt; , &lt;span class="s1"&gt;'{print $2}'&lt;/span&gt; | xargs | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'s/\ /+/g'&lt;/span&gt; | bc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



</description>
      <category>linux</category>
      <category>bash</category>
      <category>cli</category>
    </item>
    <item>
      <title>Multiple Postgres databases in a single Docker container</title>
      <dc:creator>Bartosz Gordon</dc:creator>
      <pubDate>Thu, 07 Nov 2019 22:42:35 +0000</pubDate>
      <link>https://dev.to/bgord/multiple-postgres-databases-in-a-single-docker-container-417l</link>
      <guid>https://dev.to/bgord/multiple-postgres-databases-in-a-single-docker-container-417l</guid>
      <description>&lt;p&gt;This will be a short one.&lt;/p&gt;

&lt;p&gt;The official Postgres Docker &lt;a href="https://hub.docker.com/_/postgres"&gt;image&lt;/a&gt; supports a few environment variables. One of them, &lt;code&gt;POSTGRES_DB&lt;/code&gt;, is responsible for holding a database name. However, if you want your container to include more than one database (e.g &lt;strong&gt;app&lt;/strong&gt; and &lt;strong&gt;app_test&lt;/strong&gt;), you have to reach for different solutions.&lt;/p&gt;

&lt;p&gt;One of them is to create a bash script that sets up multiple databases by &lt;code&gt;psql&lt;/code&gt; command. Postgres will execute it on database startup inside the container. An example of such script, &lt;code&gt;create-multiple-postgresql-databases.sh&lt;/code&gt;, can be found &lt;a href="https://github.com/mrts/docker-postgresql-multiple-databases/blob/master/create-multiple-postgresql-databases.sh"&gt;here&lt;/a&gt;. Credits to &lt;strong&gt;mrts&lt;/strong&gt; at Github.&lt;/p&gt;

&lt;p&gt;You can place it in whatever project directory you want, although &lt;code&gt;pg-init-scripts/&lt;/code&gt; might be a good starting point. Then, to make the script visible and executable by Postgres, it has to be mounted to the &lt;code&gt;/docker-entrypoint-initdb.d&lt;/code&gt; container directory.&lt;/p&gt;

&lt;p&gt;To mount a host directory to a container directory, we have to use &lt;a href="https://docs.docker.com/storage/volumes/"&gt;volumes&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Syntax: &lt;code&gt;&amp;lt;host directory&amp;gt;:&amp;lt;container directory&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;WARNING: You cannot mount a single file to a directory, it works just for directories.&lt;/p&gt;

&lt;p&gt;Final &lt;code&gt;docker-compose.yml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3.7"&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;db&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;app_db"&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;postgres:11.5-alpine"&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./pg-init-scripts:/docker-entrypoint-initdb.d&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;POSTGRES_USER=${DB_USER}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;POSTGRES_PASSWORD=${DB_PASSWORD}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;POSTGRES_MULTIPLE_DATABASES=app,app_test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;${DB_USER}&lt;/code&gt; syntax means that the &lt;code&gt;DB_USER&lt;/code&gt; value is read from automatically loaded &lt;code&gt;.env&lt;/code&gt; file placed inside the same directory as &lt;code&gt;docker-compose.yml&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Corresponding &lt;code&gt;.env&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DB_USER=someuser
DB_PASSWORD=somepassword
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remember, you shouldn't commit &lt;code&gt;.env&lt;/code&gt; files! Create and commit &lt;code&gt;.env-example&lt;/code&gt; file with empty secret values instead.&lt;/p&gt;

&lt;p&gt;If you want to use a different path (e.g &lt;code&gt;.env-docker&lt;/code&gt;), define &lt;code&gt;env_file&lt;/code&gt; in &lt;code&gt;docker-compose.yml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;env_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./.env-docker&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, with every piece in place, run &lt;code&gt;docker-compose up&lt;/code&gt; to start the service.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

</description>
      <category>docker</category>
      <category>postgres</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How to figure out a name for your next project?</title>
      <dc:creator>Bartosz Gordon</dc:creator>
      <pubDate>Sun, 27 Oct 2019 12:46:46 +0000</pubDate>
      <link>https://dev.to/bgord/how-to-figure-out-a-name-for-your-next-project-3jjg</link>
      <guid>https://dev.to/bgord/how-to-figure-out-a-name-for-your-next-project-3jjg</guid>
      <description>&lt;p&gt;I have a confession to make. I'm unable to work on a project unless it has a name. So the ability to verbalize an idea is a crucial part of my side projects. It also makes it easier to manage them in productivity tools of all kinds.&lt;/p&gt;

&lt;p&gt;In the beginning, I have to speak of something. There's no definitive guide for finding names, and what works for me, doesn't have to work for you. Still, techniques I describe below are rather universal and applying them will increase your chances.&lt;/p&gt;

&lt;p&gt;Straight to the point, I am going to explain my thought process by example.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to figure out a name for your next project?
&lt;/h3&gt;

&lt;p&gt;Let's say we want to find a name for a new shiny &lt;em&gt;habit tracking app&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Setup&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It's important to design your environment in a way that it's least likely to distract you. Switch off your phone, don't look at Twitter, and tidy up your desk. Grab something to write on and something to write with. I prefer physical notebooks, but do whatever suits you.&lt;/p&gt;

&lt;p&gt;It may be a good idea to set up a timer for, say, 10-20 minutes.&lt;br&gt;
This will be your creative freedom time!&lt;/p&gt;

&lt;p&gt;Have a few deep breaths.&lt;/p&gt;

&lt;p&gt;Ready, steady, go! The clock is ticking.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keywords&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The first thing I do is writing a list of keywords describing the idea.&lt;br&gt;
Usually, less than ten of them are enough.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
— habit&lt;br&gt;
— tracking&lt;br&gt;
— measurement&lt;br&gt;
— progress&lt;br&gt;
— beating procrastination&lt;br&gt;
— atomic&lt;br&gt;
— automation&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Associations&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The next step is to create a list of whatever associations you may have for every keyword you listed.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
— habit (action, cycle, loop)&lt;br&gt;
— tracking (follower, chart, line)&lt;br&gt;
— measurement (ruler, meter, mile)&lt;br&gt;
— progress (growth, steady, bar, chain)&lt;br&gt;
— beating procrastination (boxer, kick, bite, beat)&lt;br&gt;
— atomic (proton, neutron, small, tiny, electron)&lt;br&gt;
— automation (machine, computer, bot, robot, terminator)&lt;/p&gt;

&lt;p&gt;At this point, you have already established a solid base to produce lots of different names. The next step is to jot down every possible idea that comes to your mind. The important thing is not to judge any of them, not now. You can use the techniques I've listed below.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Techniques&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;— combine words&lt;br&gt;
— skip some letters&lt;br&gt;
— change order of letters&lt;br&gt;
— double some letters&lt;br&gt;
— think about emotions or feelings connected to some keyword/association&lt;br&gt;
— think about other languages (French, Latin, German, you name it)&lt;br&gt;
— think abstractly, have more associations&lt;br&gt;
— look through your window&lt;br&gt;
— look at your surroundings&lt;/p&gt;

&lt;p&gt;To give you a sense of what can be an output of following them: &lt;em&gt;habitline&lt;/em&gt;, &lt;em&gt;habeat&lt;/em&gt;, &lt;em&gt;smallbites&lt;/em&gt;, &lt;em&gt;hauto&lt;/em&gt;, &lt;em&gt;measurebot&lt;/em&gt;, &lt;em&gt;habitto&lt;/em&gt;, &lt;em&gt;habitree&lt;/em&gt;, &lt;em&gt;neutrica&lt;/em&gt;, &lt;em&gt;habiterminator&lt;/em&gt; (ok, this one is terrible :D).&lt;/p&gt;

&lt;p&gt;As soon as your timer plays the final sound, put off your pen, close your notebook or your note-taking app. Let your brain cool down after this crazy run. Don't look at the list for at least an hour. Let your relaxed thinking come in to play.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Review&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The final step is to review your ideas. You can judge them now.&lt;br&gt;
Underline the good ones, strike through the bad ones. If you have found your Holy Grail, the job had been already done! If you haven't, you can reiterate on the good ones, or repeat the process.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Thinking outside the box&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As you can imagine, not every app/company figured out their name using a technique like this. Sometimes, it doesn't have to be descriptive at all. Think about names like “Apple” for a tech company or “Amazon” for an e-commerce marketplace company. That's another route to take. There's no silver bullet.&lt;/p&gt;

&lt;p&gt;We can increase our creativity through exercise, like any other muscle.&lt;br&gt;
It's worth training, so go to the “gym” and enjoy your creative time.&lt;/p&gt;

&lt;p&gt;PS. Buy domain names as soon as possible ;)&lt;/p&gt;

&lt;p&gt;Happy brainstorming!&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>creativity</category>
      <category>writing</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Let's write a useImageZoom React hook</title>
      <dc:creator>Bartosz Gordon</dc:creator>
      <pubDate>Sat, 29 Jun 2019 08:40:28 +0000</pubDate>
      <link>https://dev.to/bgord/let-s-write-a-useimagezoom-react-hook-5354</link>
      <guid>https://dev.to/bgord/let-s-write-a-useimagezoom-react-hook-5354</guid>
      <description>&lt;p&gt;In this article, I am going to present a basic React hook responsible for handling image "zoom" functionality. &lt;/p&gt;

&lt;h2&gt;
  
  
  What we are going to build
&lt;/h2&gt;

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

&lt;p&gt;The goal is to create a &lt;code&gt;useImageZoom&lt;/code&gt; hook that provides three handlers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;zoom in&lt;/li&gt;
&lt;li&gt;zoom out&lt;/li&gt;
&lt;li&gt;reset zoom&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Bearing that in mind, we can already plan the API that hook may expose.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;zoomStyles&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;handlers&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useImageZoom&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Following prevailing convention, it returns a two-element array. &lt;/p&gt;

&lt;p&gt;A first element is a style object applied to an image, and the second element contains the aforementioned handlers. &lt;/p&gt;

&lt;h2&gt;
  
  
  Hook implementation
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// App.jsx&lt;/span&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="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;useImageZoom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;maxZoomLevel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&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;minZoomLevel&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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;zoomLevel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setZoomLevel&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;minZoomLevel&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;zoomIn&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;setZoomLevel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;zoomLevel&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
      &lt;span class="nx"&gt;zoomLevel&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;maxZoomLevel&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;zoomLevel&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="nx"&gt;zoomLevel&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;zoomOut&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;setZoomLevel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;zoomLevel&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
      &lt;span class="nx"&gt;zoomLevel&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;minZoomLevel&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;zoomLevel&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="nx"&gt;minZoomLevel&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;resetZoom&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;setZoomLevel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;minZoomLevel&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;zoomStyles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`scale(&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;zoomLevel&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handlers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;zoomIn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;zoomOut&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;resetZoom&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;zoomStyles&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;handlers&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;A few words about the implementation.&lt;/p&gt;

&lt;p&gt;If we want CSS to handle the image scaling, we need to use &lt;code&gt;transform&lt;/code&gt; property and pass&lt;code&gt;scale(x)&lt;/code&gt; as a value. By default, &lt;code&gt;x&lt;/code&gt; equals &lt;strong&gt;1&lt;/strong&gt;, so we assign &lt;strong&gt;1&lt;/strong&gt; to the &lt;code&gt;minZoomLevel&lt;/code&gt; variable. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;maxZoomLevel&lt;/code&gt; is the only parameter that hook accepts, defaults to &lt;strong&gt;5&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;zoomIn&lt;/code&gt; and &lt;code&gt;zoomOut&lt;/code&gt; functions we respectively increase and decrease the zoom level, but keeping it in &lt;code&gt;maxZoomLevel&lt;/code&gt; and &lt;code&gt;minZoomLevel&lt;/code&gt; boundary.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;resetZoom&lt;/code&gt; simply sets the &lt;code&gt;minZoomLevel&lt;/code&gt; value.&lt;/p&gt;

&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// App.jsx&lt;/span&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="c1"&gt;// function useImageZoom(maxZoomLevel = 5) {&lt;/span&gt;
&lt;span class="c1"&gt;//   ...&lt;/span&gt;
&lt;span class="c1"&gt;// }&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&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;zoomStyles&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;handlers&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useImageZoom&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"app"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"buttons"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handlers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;zoomIn&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Zoom in&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handlers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;zoomOut&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Zoom out&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"preview"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;img&lt;/span&gt;
          &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&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;zoomStyles&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handlers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resetZoom&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"https://placeimg.com/150/150/arch"&lt;/span&gt;
          &lt;span class="na"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"preview box"&lt;/span&gt;
        &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;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="nx"&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="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;App&lt;/span&gt; &lt;span class="p"&gt;/&amp;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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* styles.css */&lt;/span&gt;

&lt;span class="nc"&gt;.app&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;flex-direction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;column&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;align-items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="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="nl"&gt;text-align&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.buttons&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;margin-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;50px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.preview&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;align-items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;justify-content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;300px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;300px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;margin-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;50px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#eee&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;overflow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;relative&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;Let's see it in action.&lt;/p&gt;

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

&lt;p&gt;As you may have noticed, we have a problem. Both sides of the image get incrementally cropped as we zoom in. &lt;/p&gt;

&lt;h2&gt;
  
  
  Improvements
&lt;/h2&gt;

&lt;p&gt;The issue is that &lt;code&gt;transform-origin&lt;/code&gt;'s initial value equals &lt;code&gt;"50% 50%"&lt;/code&gt;, which is equivalent to the center of a given image. To get a sense of what this property does, please refer to &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/transform-origin"&gt;this page&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;To apply the solution, change &lt;code&gt;zoomStyles&lt;/code&gt; object to the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;zoomStyles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`scale(&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;zoomLevel&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="na"&gt;transformOrigin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;top left&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's see what happens.&lt;/p&gt;

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

&lt;p&gt;Ok, the image does not get cropped, but there is another problem. Have you noticed that there is a blank space on the left and at the top part of the preview box? We can force the image to fill the whole available space but still preserving the initial centered position. &lt;/p&gt;

&lt;p&gt;We can do it by positioning the image absolutely in the top left corner of the preview box but only if zoom level greater than 1 is applied.&lt;/p&gt;

&lt;p&gt;Change &lt;code&gt;zoomStyles&lt;/code&gt; object to the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;zoomStyles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`scale(&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;zoomLevel&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="na"&gt;transformOrigin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;top left&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;zoomLevel&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;absolute&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;zoomLevel&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;zoomLevel&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Voilà, it works!&lt;/p&gt;

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

&lt;p&gt;The final version is available on &lt;a href="https://codesandbox.io/s/epic-moon-suhcj?fontsize=14"&gt;codesandbox&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In the next article, we are going to make &lt;code&gt;useImageZoom&lt;/code&gt; hook more flexible and easier to use. Stay tuned! &lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Simplify repetitive Jest test cases with test.each</title>
      <dc:creator>Bartosz Gordon</dc:creator>
      <pubDate>Mon, 06 May 2019 20:02:47 +0000</pubDate>
      <link>https://dev.to/bgord/simplify-repetitive-jest-test-cases-with-test-each-310m</link>
      <guid>https://dev.to/bgord/simplify-repetitive-jest-test-cases-with-test-each-310m</guid>
      <description>&lt;h3&gt;
  
  
  Problem
&lt;/h3&gt;

&lt;p&gt;From time to time, I run into a situation, where most of my test cases follow a similar sequence of steps. This scenario most often happens while unit testing helpers/utility functions. Given certain arguments, check if the actual result is equal to the expected result. Over and over again. As the number of cases grows, the test suite can get bloated. &lt;/p&gt;

&lt;p&gt;Contrived example ahead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;add&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;'add' utility&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;given 2 and 2 as arguments, returns 4&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="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;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;add&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="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&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;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;given -2 and -2 as arguments, returns -4&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="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;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&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="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;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;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;given 2 and -2 as arguments, returns 0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="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;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;add&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="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;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;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Solution
&lt;/h3&gt;

&lt;p&gt;I thought about an abstraction to avoid this kind of boilerplate, and after a few google searches, I found the &lt;a href="https://jestjs.io/docs/en/api#testeachtable-name-fn-timeout"&gt;test.each&lt;/a&gt; Jest utility. &lt;/p&gt;

&lt;p&gt;This helper encourages you to create the array of &lt;code&gt;cases&lt;/code&gt;, where you store arguments and expected results, and then iterate through the entire array to run the tested function and assert the results.&lt;/p&gt;

&lt;p&gt;Example with &lt;code&gt;test.each&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;add&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;b&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;cases&lt;/span&gt; &lt;span class="o"&gt;=&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="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&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="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="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;],&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="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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]];&lt;/span&gt;

&lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;'add' utility&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cases&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;given %p and %p as arguments, returns %p&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;firstArg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;secondArg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expectedResult&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;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;firstArg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;secondArg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&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;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expectedResult&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Notes
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Benefits&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;easier to add new tests cases&lt;/li&gt;
&lt;li&gt;less boilerplate&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Possible drawback&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;more abstractions, some people may find it unnecessary&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I find it worthwhile to write a comment about the items of the &lt;code&gt;cases&lt;/code&gt; array to increase readability and reduce mental effort.&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;// first argument, second argument, expected result&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cases&lt;/span&gt; &lt;span class="o"&gt;=&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="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&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="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="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;],&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="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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>testing</category>
      <category>javascript</category>
      <category>jest</category>
    </item>
    <item>
      <title>How do I identify my knowledge gaps and learn?</title>
      <dc:creator>Bartosz Gordon</dc:creator>
      <pubDate>Sat, 26 Jan 2019 08:24:29 +0000</pubDate>
      <link>https://dev.to/bgord/how-do-i-identify-my-knowledge-gaps-and-learn-4mlc</link>
      <guid>https://dev.to/bgord/how-do-i-identify-my-knowledge-gaps-and-learn-4mlc</guid>
      <description>&lt;p&gt;In this article, I'm going to present you my workflow for gathering topics and a way to efficiently learn them. &lt;/p&gt;

&lt;p&gt;No one knows everything, that's certain. Every day, I find myself not knowing something, be it a syntax, concept or a pattern. Some missing answers are one google (or duckduckgo) search away, so it would probably be a waste of memory and effort to learn by heart anything I'm able to find under one minute. &lt;br&gt;
But what about concepts? Yeah, most of them are impossible to be "found" in a very short time and require a significant amount of learning effort.&lt;/p&gt;

&lt;p&gt;Disclaimer: it's a documentation of my processes — they work for me, but not necessarily must suit you.&lt;/p&gt;
&lt;h1&gt;
  
  
  Identifying knowledge gaps.
&lt;/h1&gt;

&lt;p&gt;If I want to become better in some field, I need to know what I don't know. I answered the following questions and tried not to be overly specific. It's still early stage of the entire process, and I wanted to see a big picture only.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;What &lt;strong&gt;new&lt;/strong&gt; technology/concept would be certainly profitable in my day to day job in the next 3-6 months?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;What field do I want to &lt;strong&gt;deepen&lt;/strong&gt; my knowledge in?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;What piece of technology/concept &lt;strong&gt;excites&lt;/strong&gt; me and makes me want to try it?&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;After writing the answers down, I gave myself a couple of days to digest it and to remove some unnecessary/outdated points. After tidying up, I progressed to the next point.&lt;/p&gt;
&lt;h1&gt;
  
  
  Refinement
&lt;/h1&gt;

&lt;p&gt;At first, my list looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. What new technology/concept would be certainly profitable in my day to day job in the next 3-6 months?

* TypeScript
* Docker
* Observables
* state charts/finite automata

2. What field do I want to **deepen** my knowledge in?

* maintainable CSS and accessibility
* new React patterns (hooks, Suspense)
- better testing (JS/React)
* Git
* more of an advanced vanilla JS and functional concepts/patterns
* Bash scripting
* Vim/Tmux workflow
* UNIX command line programs (sed, grep, awk, etc.)
* RegExps
* Vue
* Nginx

3. What piece of technology/concept **excites** me and makes me want to try it?

* GraphQL
* Rust/Go/Elixir
* basic machine learning algorithms
* designing and prototyping with Figma
* D3.js

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

&lt;/div&gt;



&lt;p&gt;The next thing I did was prioritization. I sorted each of the three lists by the importance of a topic.&lt;/p&gt;

&lt;p&gt;When I start learning about a new thing, there is so much going on in my head. I wanted to find a technique to help me feel less overwhelmed and to be able to tackle a few smaller problems instead of one big one. &lt;/p&gt;

&lt;p&gt;What is probably obvious, a point sounding like "Docker" at first doesn't seem approachable, but a question "How to set up a Docker container for a Node.js app printing "Hello world" to the console?" seems like a good starting point.&lt;/p&gt;

&lt;p&gt;To track my journey, I created a git repository and the README file became my "Roadmap for 2019". I extracted every point from the list to a separate section, so I can add questions to each of them.&lt;/p&gt;

&lt;p&gt;That's the important part: as fast as I identify a question, I add it to the list under the matching section to keep it up to date.&lt;/p&gt;

&lt;p&gt;An example of how a basic section could look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# TypeScript (0/5)

* How to set up React app with TypeScript?
* How to add types for primitive values like string/number/boolean to a single variable?
* How to type objects?
* How to type functions? What about optional and default parameters?
* What's the difference between type/interface?
* How to type React classes and function components?
* What is a type inference?
* What are generic types and how to use them?

and so on...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I tried to get out of my head as many points as possible. It was very freshening to see those questions in one place. I realized that after I answer them, I can be kind of fluent in TypeScript. Awesome!&lt;/p&gt;

&lt;p&gt;I refine my Roadmap (almost) every day, it includes rephrasing and breaking down the questions to make them more approachable and specific.&lt;/p&gt;

&lt;h1&gt;
  
  
  Learning
&lt;/h1&gt;

&lt;p&gt;It gives me the most value when I apply the knowledge to some real-life project or create some sort of snippet with it (a way of avoiding "dry" learning).&lt;/p&gt;

&lt;p&gt;In order to retain my Q&amp;amp;As, I create a separate markdown file for each section from the Roadmap. Every time I learn something, I write it to the corresponding file. It gives me easy access every time later. Keep it practical.&lt;/p&gt;

&lt;p&gt;An example of the answer looks like the following one.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;**What is a difference between arguments and parameters?**

Parameters are variables in funcion definition.

function add(x, y) { // x and y are parameters
  return x + y;
}

Arguments are variables passed down to a function in place of parameters.

add(2, 3); // arguments
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;I schedule one major item from the Roadmap for each day. It's great if I manage to learn more than that, but the world is not perfect and neither am I. That's definitely enough, still making progress anyways. Imagine learning 365 concepts/techniques in one year, apart from the job or university! &lt;/p&gt;

&lt;p&gt;It's important to make it clear when and where I'm going to spend my time studying. Make it as easy as possible to establish this routine as your habit. The last thing before closing my laptop in the evening is turning on a text editor and a browser. Then in the morning after I wake up and have my morning routine, I sit at the desk and learn for at least 20 minutes.&lt;/p&gt;

&lt;p&gt;After 2-3 weeks, I'm able to set up a Docker container for Node.js &amp;amp; MongoDB projects, I can type intermediate React/Redux &amp;amp; Redux/Hooks apps, and got better at my Vim/Tmux workflow (it includes in bash scripting).&lt;br&gt;
I was also able to clearly (it was the feedback I got) explain some non-trivial git and HTML&amp;amp;CSS concepts to a beginner (and learnt more of an accessibility myself). All of this apart from the things I learnt in my day to day job.&lt;/p&gt;

&lt;p&gt;Small pieces compound. &lt;/p&gt;

&lt;p&gt;Regarding the topic of habits, I recommend the "Atomic Habits" book by James Clear, it helped me a ton in coming up with my workflow.&lt;/p&gt;

&lt;p&gt;BONUS: you can try to create ANKI card decks for remembering concepts that you learn. You can learn more about spaced repetition &lt;a href="https://medium.freecodecamp.org/use-spaced-repetition-with-anki-to-learn-to-code-faster-7c334d448c3c"&gt;here&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>learning</category>
      <category>programming</category>
      <category>productivity</category>
      <category>devtips</category>
    </item>
    <item>
      <title>Going (almost) mouseless: browsers </title>
      <dc:creator>Bartosz Gordon</dc:creator>
      <pubDate>Sat, 19 Jan 2019 09:47:00 +0000</pubDate>
      <link>https://dev.to/bgord/going-almost-mouseless-browsers--5c1h</link>
      <guid>https://dev.to/bgord/going-almost-mouseless-browsers--5c1h</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;In this post, I'm going to use the Firefox browser's shortcuts as an example. Most of them should work in other — webkit based —  browsers too. Prefix for given shortcut may differ based on the operating system.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Originally posted on &lt;a href="https://medium.com/@bargord11/going-almost-mouseless-browsers-e83c7f48b046"&gt;Medium&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why even bother?
&lt;/h2&gt;

&lt;p&gt;The reason I'm writing this article is that I see a field for improvement when it comes to performance during "standard" — mostly mouse-based — navigation.&lt;/p&gt;

&lt;p&gt;Of course, not every piece of software has shortcuts, and that's absolutely fine. I imagine using e.g a graphic design app without mouse could be problematic and inefficient. But most of the time, given program shares a handful of them.&lt;/p&gt;

&lt;p&gt;The good example may be your browser.&lt;/p&gt;

&lt;p&gt;In this article, you're going to learn just enough of shortcuts to perform most frequent tasks like tabs and windows management or powerful text editing. They're grouped into 4 lessons, so you can approach them incrementally.&lt;/p&gt;

&lt;p&gt;After you give your navigation control over to the keyboard, you will (probably) never look back.&lt;br&gt;
Being able to do the aforementioned things without:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;moving your hands off the keyboard to the mouse&lt;/li&gt;
&lt;li&gt;placing the cursor&lt;/li&gt;
&lt;li&gt;getting your hands back on the keyboard
improved my workflow and speed tremendously. This approach shines the most when you're performing many of operations in the row. Saved seconds add up!&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Process of learning
&lt;/h2&gt;

&lt;p&gt;The way I approach learning a new set of shortcuts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;write down a few of the shortcuts you want to learn&lt;/li&gt;
&lt;li&gt;put them in a visible place — a wallpaper/note/post-it card near your desk&lt;/li&gt;
&lt;li&gt;practice until you get comfortable with them.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Then repeat the entire process starting from the first point. After some time, your muscle memory will make it a no-brainer for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tab management
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;LEVEL 1&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open a new tab — &lt;code&gt;Ctrl + T&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Close current tab — &lt;code&gt;Ctrl + W&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Restore last closed tab — &lt;code&gt;Ctrl + Shift + T&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Open a new window — &lt;code&gt;Ctrl + N&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Reload a page — &lt;code&gt;Ctrl + R&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These five commands should already improve your tab management speed. As a bonus point, you can refresh a current tab, and restore the previously closed tab, nerves saved!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;LEVEL 2&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go to the n-th tab — &lt;code&gt;Alt + N&lt;/code&gt; (works for 1st-8th tab)&lt;/li&gt;
&lt;li&gt;Go to the last tab — &lt;code&gt;Alt + 9&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Move current tab to the left — &lt;code&gt;Ctrl + Shift + PgUp&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Move current tab to the right right — &lt;code&gt;Ctrl + Shift + PgDn&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Focus address line — &lt;code&gt;Ctrl + L&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With this five shortcuts, you will be able to focus given tab and move around the tabs themselves. As a bonus point, accessing the address line has been made easier.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;LEVEL 3&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go to the next focusable element on page — &lt;code&gt;Tab&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Go to the previous focusable element on page — &lt;code&gt;Shift + Tab&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Go back to the previous page — &lt;code&gt;Ctrl + [&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Go forward to the next page — &lt;code&gt;Ctrl + ]&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Open a new private window — &lt;code&gt;Ctrl + Shift + P&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By this level, you'll be able to jump between links, inputs, and other interactive elements on the page. Also, you can navigate back and forth in the current tab browsing history. Wanna open a new private window? You've got it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;LEVEL 4&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go to the next word — &lt;code&gt;Ctrl + Arrow Right&lt;/code&gt; (when editing text)&lt;/li&gt;
&lt;li&gt;Go to the previous word — &lt;code&gt;Ctrl + Arrow Left&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Go to the last word — &lt;code&gt;Ctrl + A + Arrow Right&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Go to the first word — &lt;code&gt;Ctrl + A + Arrow Left&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Open history — &lt;code&gt;Ctrl + H&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This set of shortcuts will make you more proficient during text navigation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;BONUS&lt;/strong&gt;: In case you're familiar with VI/VIM, I recommend using an extension that emulates its keybindings. You'll probably find a decent one for your browser. Although usually it's not a one to one mapping.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>browsers</category>
    </item>
  </channel>
</rss>
