<?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: Victoria Lo</title>
    <description>The latest articles on DEV Community by Victoria Lo (@lo_victoria2666).</description>
    <link>https://dev.to/lo_victoria2666</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%2F469013%2Fa354e070-b8ab-4f4d-ab60-9861263459b1.jpg</url>
      <title>DEV Community: Victoria Lo</title>
      <link>https://dev.to/lo_victoria2666</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/lo_victoria2666"/>
    <language>en</language>
    <item>
      <title>My New Year Hackathon Project: Image Classification Pokédex</title>
      <dc:creator>Victoria Lo</dc:creator>
      <pubDate>Mon, 04 Jan 2021 09:55:19 +0000</pubDate>
      <link>https://dev.to/lo_victoria2666/my-new-year-hackathon-project-image-classification-pokedex-4b54</link>
      <guid>https://dev.to/lo_victoria2666/my-new-year-hackathon-project-image-classification-pokedex-4b54</guid>
      <description>&lt;p&gt;Hello everyone! Today, I want to share what I built over the weekend for the New Year New Hack hackathon! It was an amazing experience and a fun way to start the new year.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LfHPPCTY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1609673264920/jLOKnsIbf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LfHPPCTY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1609673264920/jLOKnsIbf.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  My Project
&lt;/h2&gt;

&lt;p&gt;It is a web app that is able to identify the original 151 species of Pokémon using a custom image classification model. The user will first supply an image, it can be a 2D image or from a camera. Then, click on 'Run Pokédex Analysis'. The model will return its prediction and then the app will fetch the data of that predicted Pokémon. The data is displayed onto the Pokédex Analysis Report page and the app will read aloud the Pokémon's name, type and description.&lt;/p&gt;

&lt;p&gt;Here's a demo clip of the process (no audio because it is a GIF):&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bYbgcdjd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1609675462307/t4jXA15vH.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bYbgcdjd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1609675462307/t4jXA15vH.gif" alt="ezgif.com-gif-maker.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Motivation
&lt;/h3&gt;

&lt;p&gt;On the web, a 'Pokédex' is just a website where users can search and view any Pokémon's data. But in my childhood, a Pokédex refers to the cool electronic device that can recognize the Pokémon it is pointing to and returns that Pokémon information in a robotic voice. And so, the motivation behind this project was to make my childhood dream into reality.&lt;/p&gt;

&lt;p&gt;Here's how a "real" Pokédex works (no audio, it's a GIF):&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jIzVlgad--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1609745450225/DdQ3PJqqU.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jIzVlgad--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1609745450225/DdQ3PJqqU.gif" alt="ezgif.com-gif-maker.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  How I Built It
&lt;/h3&gt;

&lt;h4&gt;
  
  
  1. Gathering Images
&lt;/h4&gt;

&lt;p&gt;First, I had to build a custom image classification model, one that knows every 151 species. So I got down to business and start collecting Pokémon images.&lt;/p&gt;

&lt;p&gt;I gathered various types of pictures, not just simple 2D images like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vSlDXDzd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1609745600210/S7guyPhtq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vSlDXDzd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1609745600210/S7guyPhtq.png" alt="image.png"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The model should recognize this is Pikachu.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;But also ones that are from movies, games, cards, goods, etc.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mym4sbqQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1609745683761/fFohrlxtU.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mym4sbqQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1609745683761/fFohrlxtU.png" alt="image.png"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The model should also recognize this is Pikachu.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In total, I had over 10,000 images of Pokémon for the 151 species that the model will learn. 10,216 to be exact.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. AutoML Vision
&lt;/h4&gt;

&lt;p&gt;Then, I used Google Cloud AutoML VIsion API to label each image to their appropriate Pokémon name.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PEMH7gCr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1609746050962/Xv8igk-ec.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PEMH7gCr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1609746050962/Xv8igk-ec.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After that, I started training the model. Due to the time limitations for this 48-hour hackathon, I could only train twice but both times achieve identical precision results, probably because I still use the same training data.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lOyz-y1f--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1609746191488/4fF-LKNqu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lOyz-y1f--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1609746191488/4fF-LKNqu.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  3. Export model
&lt;/h4&gt;

&lt;p&gt;Once I'm satisfied with the model, I exported it as a Tensorflow.js package to use in the browser as a web app.&lt;/p&gt;

&lt;h4&gt;
  
  
  4. The App
&lt;/h4&gt;

&lt;p&gt;Building the app is the simplest part of this project. My team and I use TailwindCSS  for styling, Chart.js to display interactive charts and axios to fetch data from PokéAPI.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Z088XmIE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1609753609040/E4AmCa93R.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Z088XmIE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1609753609040/E4AmCa93R.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For our app to talk, we use Cloud Text-to-Speech API and ta-da! The first image classifying Pokédex is born!&lt;/p&gt;

&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;p&gt;Due to lack of time, some Pokémon species had more training data than others. While the app generally return correct results, it also had funny ones. &lt;/p&gt;

&lt;h3&gt;
  
  
  Test Highlights - Difficulty: Easy
&lt;/h3&gt;

&lt;p&gt;First, I tested with 2D images of Pokémon, the app returns and displays the correct Pokémon at almost 99% accuracy. So I decided to use camera images to test how truly accurate the model is.&lt;/p&gt;

&lt;h3&gt;
  
  
  Test Highlights - Difficulty: Medium
&lt;/h3&gt;

&lt;p&gt;Lucky for me, I had a few Pokémon objects lying around my house to serve as my testing data. The first tests was front-facing Pokémon plushes. Even with complex background behind, it classifies all of them accurately.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zS24pMCA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1609752916064/BHG-xIQ6K.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zS24pMCA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1609752916064/BHG-xIQ6K.png" alt="image.png"&gt;&lt;/a&gt;&lt;br&gt;
Images from left to right: the model correctly identifies Slowpoke, Vaporeon and Snorlax.&lt;/p&gt;

&lt;h3&gt;
  
  
  Test Highlights - Difficulty: Hard
&lt;/h3&gt;

&lt;p&gt;At off-angles and many items in the background, the model shows that it can still recognize some Pokémon, but not all.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--v2KRVipW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1609753096783/exvr6e-tr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--v2KRVipW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1609753096783/exvr6e-tr.png" alt="image.png"&gt;&lt;/a&gt;&lt;br&gt;
Images from left to right: the model incorrectly identifies the Abra's side view as an Alakazam, but correctly identifies Flareon's side view as a Flareon.&lt;/p&gt;

&lt;p&gt;Even for Pokémon with different colours, the model still proved pretty good accuracy.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vWbXllA9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1609753186413/97R18eJk8.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vWbXllA9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1609753186413/97R18eJk8.jpeg" alt="vulpix.jfif"&gt;&lt;/a&gt;&lt;br&gt;
In the picture taken above, the model correctly identifies Vulpix, even though its original colour is orange, not white.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Save all audio files
&lt;/h3&gt;

&lt;p&gt;Because the Text-to-Speech API requires authentication and an access token, our app will not have Text-to-Speech working if someone other than my team and I uses it.&lt;/p&gt;

&lt;p&gt;A potential solution to this problem is to download all 151 Pokémon species' audio files beforehand and just play the audio when that Pokémon's data is displayed.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. A 'Not Pokémon' result
&lt;/h3&gt;

&lt;p&gt;Currently, the model can only return a Pokémon, which means that even if the test image is not a Pokémon, it will return its best guess.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bxu_iUE7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1609753516118/pLHJR19Yx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bxu_iUE7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1609753516118/pLHJR19Yx.png" alt="image.png"&gt;&lt;/a&gt;&lt;br&gt;
From left to right, Mario is classified as a Krabby and Koya is classified as a Squirtle even though they are non-Pokemon.&lt;/p&gt;

&lt;p&gt;A next step for this app is to fix this issue by allowing the model to return 'Not Pokémon' if the confidence level for its best guess is below a certain threshold. That way, it won't classify random things as Pokémon.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Identifying Multiple Pokémon in 1 image
&lt;/h3&gt;

&lt;p&gt;So what happens if a picture like the one below is supplied by the user?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--cPbwBUWT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1609750335848/VQoBCNMrj.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cPbwBUWT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1609750335848/VQoBCNMrj.jpeg" alt="3poke.jfif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The model will return the most apparent Pokémon in the image. In this case, Bulbasaur because he is in the middle.&lt;/p&gt;

&lt;p&gt;This can be an issue if the user supplied a picture with many Pokémon in it. Hence, as a next step, it is essential to make the model recognize multiple Pokémon and maybe a return a message to re-take the photo, supply a new image or something.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Live Camera Mobile Version
&lt;/h3&gt;

&lt;p&gt;The final step for this app is not an issue fix, but an expansion. A true Pokédex has to be a portable device you can carry around. So my team and I are not only planning to include more Pokémon in the model (there's actually over 900 species of them), we are also planning to build a mobile app version with live camera detection.&lt;/p&gt;

&lt;p&gt;That would really make any phone feel like a real Pokédex!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--IAhCMPqT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1609752557404/8z1WRMpej.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IAhCMPqT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1609752557404/8z1WRMpej.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Ultimately, it's all about having fun while learning something new. We are humbled that this project, that only started because of our childhood dream, has gotten us an astonishing 3rd place in the hackathon. &lt;/p&gt;

&lt;p&gt;But this is just the beginning, to a developer's journey to build something awesome every day. Thanks for reading, happy new year and cheers!&lt;/p&gt;




&lt;h3&gt;
  
  
  Links
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Github repo: &lt;a href="https://github.com/victoria-lo/image-classifying-pokedex"&gt;https://github.com/victoria-lo/image-classifying-pokedex&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Devpost submission: &lt;a href="https://devpost.com/software/image-classifying-pokedex"&gt;https://devpost.com/software/image-classifying-pokedex&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;PokeAPI: &lt;a href="https://pokeapi.co/"&gt;https://pokeapi.co/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;AutoML Vision: &lt;a href="https://cloud.google.com/automl/"&gt;https://cloud.google.com/automl/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Chart.js: &lt;a href="https://www.chartjs.org/"&gt;https://www.chartjs.org/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>hackathon</category>
      <category>machinelearning</category>
      <category>googlecloud</category>
      <category>todayilearned</category>
    </item>
    <item>
      <title>How I Built My First Trading Bot For Hashnode's Hackathon</title>
      <dc:creator>Victoria Lo</dc:creator>
      <pubDate>Mon, 28 Dec 2020 14:07:07 +0000</pubDate>
      <link>https://dev.to/lo_victoria2666/how-i-built-my-first-trading-bot-for-hashnode-s-hackathon-2e1l</link>
      <guid>https://dev.to/lo_victoria2666/how-i-built-my-first-trading-bot-for-hashnode-s-hackathon-2e1l</guid>
      <description>&lt;p&gt;Hello everyone! I'm excited to showcase a mini project that I've built for Hashnode's Christmas Hackathon. I will be explaining the inspiration behind this project, how I built it, some challenges I faced along the way and what I've learnt from partaking in this spontaneous hackathon. Let's begin!&lt;/p&gt;

&lt;h3&gt;
  
  
  Inspiration
&lt;/h3&gt;

&lt;p&gt;Financial Technology aka &lt;strong&gt;FinTech&lt;/strong&gt; is a broad term for financial services or products that integrates technology to automate and optimize its uses. As a developer, I've always been interested in stocks and investments; in particular, the use of robo-advisors and trading bots today have piqued my curiosity for quite a while.&lt;/p&gt;

&lt;p&gt;Hence, by participating in Hashnode's Hackathon, I thought it will be a good opportunity for me to try something new as a fun challenge.&lt;/p&gt;

&lt;h3&gt;
  
  
  How I Built It
&lt;/h3&gt;

&lt;p&gt;The algorithm is created and build on &lt;a href="https://www.quantconnect.com/"&gt;QuantConnect (QC)&lt;/a&gt; -  a browser-based algorithmic trading platform. It is a simple yet powerful platform that empowers anyone to build, backtest and execute their own trading algorithms.&lt;/p&gt;

&lt;p&gt;QC supports both Python and C#. I signed up for a free account. Then I spend the next few hours learning on how to navigate and use the platform via its bootcamp courses. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--aIC1lJcH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1608706224322/-c8Fajfoj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--aIC1lJcH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1608706224322/-c8Fajfoj.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At first glance, it seems overwhelming to use. Just understanding all the tools, different modules and strategies available for use is difficult, maybe even more so for someone without financial knowledge. I recommend reading up on some finance 101 books to know what is a close, open, high, low, RSI, MACD, Bollinger Bands, etc. or else it will be too confusing to learn both the technical and financial aspects at the same time.&lt;/p&gt;

&lt;p&gt;After familiarizing myself with the platform, I got down to business (kind of). &lt;/p&gt;

&lt;h3&gt;
  
  
  Challenges
&lt;/h3&gt;

&lt;p&gt;The biggest challenge I faced when doing this hackathon is definitely &lt;strong&gt;time management&lt;/strong&gt;. Time was a precious resource in my case. I have a full-time job, as well as other commitments, and so I had to allocate my time wisely and progress patiently on this project. When I have decided to build a trading bot as the project for this hackathon, I researched whenever I am free for 2 days before finally discovering and deciding to go with QC.&lt;/p&gt;

&lt;p&gt;Another unforgettable challenge was &lt;strong&gt;researching and deep-diving&lt;/strong&gt; into a not-so-familiar territory for me: the QC platform and trading strategies. The QC platform was incredibly overwhelming, I had to spend hours just reading the documentation to understand their modules and know how to apply them. I have zero clue on how to design profitable algorithms and testing them.&lt;/p&gt;

&lt;p&gt;I looked up various trading strategies like pairs trading, momentum-based, short-term reversals, mean-reverting and so on. Risk and return are trade-offs to be carefully managed in every strategy. That is something that I have to bear in mind when implementing my trading bot, and is quite a huge challenge for a beginner like me.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SNYgMPKz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1608730602750/Z1nffuJ_5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SNYgMPKz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1608730602750/Z1nffuJ_5.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: some articles/papers I found helpful are listed below in the Resources section.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Results &amp;amp; Next Steps
&lt;/h3&gt;

&lt;p&gt;After testing my algorithm for several rounds of backtesting, it is clear that the algorithm is unstable. The results show that it is unreliable for live trading and definitely needs more improvement to work on (see Exhibits below).&lt;/p&gt;

&lt;h4&gt;
  
  
  Exhibit A: Worst profits
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3w8dunMc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1608777287889/1j5PdKKna.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3w8dunMc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1608777287889/1j5PdKKna.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Exhibit B: Best profits
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3moWIoyI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1608805534180/3IltqI2d2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3moWIoyI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1608805534180/3IltqI2d2.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The next steps in this project would be to &lt;strong&gt;get more financial expertise&lt;/strong&gt; (from a friend perhaps) to be able to build a better bot. I noticed that on test runs where the returns are high, it is most likely due to overfitting and not because the bot was doing well. Therefore, learning the various modules (i.e. machine learning) and trading strategies on QC will be a good idea to progress and test the algorithm more smoothly. &lt;/p&gt;

&lt;p&gt;Overall, this hackathon sure teaches me that &lt;strong&gt;there is no limit to learning&lt;/strong&gt; as a developer. Technology can be found integrated in various sectors, such as Finance. Building a product that synergizes in both the tech and finance industry was thrilling yet challenging. I sure hope to continue learning as much as I can now that this hackathon has given me a head-start. Plus, I like this hackathon allows me to spend some time with my old friend: &lt;strong&gt;Python&lt;/strong&gt;, after coding in JavaScript for a long time.&lt;/p&gt;

&lt;p&gt;Thanks for reading! I hope everyone has built something awesome and cool for Hashnode's Hackathon. I shall be reading up on the tag #christmashackathon and I look forward to more interesting reads soon! Happy holidays, stay safe and cheers!&lt;/p&gt;

&lt;h3&gt;
  
  
  Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Bland, G. (2020, November 12). QuantConnect - A Complete Guide - AlgoTrading101 Blog. Retrieved December 23, 2020, from &lt;a href="https://algotrading101.com/learn/quantconnect-guide/"&gt;https://algotrading101.com/learn/quantconnect-guide/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Faber, M. (2010, April 20). Relative Strength Strategies for Investing. Retrieved December 23, 2020, from &lt;a href="https://papers.ssrn.com/sol3/papers.cfm?abstract_id=1585517"&gt;https://papers.ssrn.com/sol3/papers.cfm?abstract_id=1585517&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Faber, Meb, A Quantitative Approach to Tactical Asset Allocation (February 1, 2013). The Journal of Wealth Management, Spring 2007, Available at SSRN: &lt;a href="https://ssrn.com/abstract=962461"&gt;https://ssrn.com/abstract=962461&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Volta, V. (n.d.). Profitable Algorithmic Trading Strategies in Mean-Reverting Markets. Retrieved December 23, 2020, from &lt;a href="https://www.academia.edu/42655318/Profitable_Algorithmic_Trading_Strategies_in_Mean_Reverting_Markets"&gt;https://www.academia.edu/42655318/Profitable_Algorithmic_Trading_Strategies_in_Mean_Reverting_Markets&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Cover image background from freepik.com&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>python</category>
      <category>christmashackathon</category>
      <category>todayilearned</category>
    </item>
    <item>
      <title>5 Ways How Blogging Changed My Life</title>
      <dc:creator>Victoria Lo</dc:creator>
      <pubDate>Thu, 24 Dec 2020 02:40:25 +0000</pubDate>
      <link>https://dev.to/lo_victoria2666/5-ways-how-blogging-changed-my-life-e8d</link>
      <guid>https://dev.to/lo_victoria2666/5-ways-how-blogging-changed-my-life-e8d</guid>
      <description>&lt;p&gt;Hello everyone! Welcome back to another &lt;a href="https://hashnode.com/series/victorias-blogging-tips-ckgcf749p06huo9s152is62qd" rel="noopener noreferrer"&gt;Blogging Tips Series&lt;/a&gt; article. &lt;/p&gt;

&lt;p&gt;Instead of tips, in this article, I'd like to share some of my personal experiences and things that I've gained ever since I've started blogging consistently. I hope by sharing this, you can be more inspired and motivated to start your own blog too in 2021.&lt;/p&gt;

&lt;p&gt;Without further ado, here are 5 ways how blogging has impacted my life.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Meeting New People
&lt;/h3&gt;

&lt;p&gt;If I have to choose the biggest change that I've experienced from blogging, it would be meeting new people. As an introvert, I tend to stay in my bubble of close friends and rarely try to meet new people.&lt;/p&gt;

&lt;p&gt;But meeting new people through blogging feels unforced and natural. I met really amazing people whom I can discuss plenty of interests and passion topics with.&lt;/p&gt;

&lt;p&gt;Also, if it weren't for blogging, I would have missed out being part of an amazing community like Hashnode. I would not have met people who are constantly inspiring and motivating me to do better.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Re-Connect with Old Friends
&lt;/h3&gt;

&lt;p&gt;Surprisingly, blogging has helped me reunite with old friends. After living in so many different countries, it is difficult for me to maintain friendships and over time, we end up drifting apart.&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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1606452073315%2F-27bTT31k.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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1606452073315%2F-27bTT31k.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Some of my old friends thankfully discovered my blog and it's so heartwarming to be able to rekindle that friendship, all thanks to blogging. We are now always sharing ideas and fun passion projects to collaborate with.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Secure more job interviews
&lt;/h3&gt;

&lt;p&gt;Before I started this blog, I was a small freelancer applying to numerous jobs every day. My resume was anything but interesting, there's nothing in particular that stand out. And so, out of 100 jobs I applied to, I'd say I only landed 15-20 interviews.&lt;/p&gt;

&lt;p&gt;If I had known that blogging could increase that 15% interview rate to 90%, I would have started sooner. This month, I applied to 20 jobs and 18 of them gave me an interview opportunity. &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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1606452472723%2Fgq18-A9yR.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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1606452472723%2Fgq18-A9yR.png" alt="interview-concept-illustration_114360-1501.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The reason: they were impressed by my blog. It tells them that I have good productivity habits and an excellent learning attitude. Actions speak louder than words on a resume.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Self-Discovery
&lt;/h3&gt;

&lt;p&gt;Before I started blogging, I had a lot of fears about writing. Fear that I was not good enough. Fear that I would be highly criticized. Fear that I could never publish an article because it had to be "perfect". Ultimately, I was afraid of letting myself down. &lt;/p&gt;

&lt;p&gt;I read "Shoe Dog" by Phil Knight recently. He talked about his struggles, how he overcome them and become Nike's founder. He said,&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Start before you're ready.&lt;/p&gt;
&lt;/blockquote&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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1606452594326%2FN_Dnots5I.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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1606452594326%2FN_Dnots5I.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you are always contemplating whether to take the first step, then that's already time wasted. Start before you're ready and make mistakes along the way. And now with blogging, I have discovered what I like about writing, the gains/lessons I've received from it and what it really means to me as a dev blogger.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Self-Improvement
&lt;/h3&gt;

&lt;p&gt;As I embark on this blogging journey, I learned how to celebrate small progress and focus on personal growth.&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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1606452707539%2Fif6xODZrw.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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1606452707539%2Fif6xODZrw.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I explored my creativity by &lt;strong&gt;learning new skills&lt;/strong&gt; like Photoshop to design my cover arts and developing a writing style that suits my identity. &lt;/p&gt;

&lt;p&gt;My &lt;strong&gt;time management and discipline&lt;/strong&gt; also improves naturally as I tried to blog consistently. Becoming more open-minded and accepting to give and receive well-intended feedback is also another self-improvement aspect.&lt;/p&gt;

&lt;p&gt;Last but not least, by searching within myself for &lt;strong&gt;my motivation to write&lt;/strong&gt;, I learned how to overcome my perfectionist mindset and improve that imposter's syndrome we all often get now and then.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finally...
&lt;/h2&gt;

&lt;p&gt;It's about patience. Patience to grow and progress. Patience to overcome and better yourself. Patience to meet the people you can truly connect with.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Patience is not passive, on the contrary, it is concentrated strength."  - Bruce Lee&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Above all, blogging has become a true joy, a small getaway where I can indulge in my own thoughts. Without a doubt, it has changed my life.&lt;/p&gt;

&lt;p&gt;Thank you for reading this rather long article. The purpose of this article is to reflect and be grateful of all the things blogging has given me. If you'd like to reflect too, and share anything that changed for you since you've started blogging, please feel free to share in the comments below! Cheers!&lt;/p&gt;

</description>
      <category>blogging</category>
      <category>generaladvice</category>
      <category>personal</category>
      <category>technicalwriting</category>
    </item>
    <item>
      <title>5 Common Beginner Blogging Mistakes</title>
      <dc:creator>Victoria Lo</dc:creator>
      <pubDate>Thu, 17 Dec 2020 03:09:01 +0000</pubDate>
      <link>https://dev.to/lo_victoria2666/5-common-beginner-blogging-mistakes-1dpb</link>
      <guid>https://dev.to/lo_victoria2666/5-common-beginner-blogging-mistakes-1dpb</guid>
      <description>&lt;p&gt;Hello everyone! Welcome to another article on &lt;a href="https://hashnode.com/series/victorias-blogging-tips-ckgcf749p06huo9s152is62qd" rel="noopener noreferrer"&gt;Victoria's Blogging Tips&lt;/a&gt;. First of all, I'd just like to say thank you to those who supported and enjoyed this series. I'm glad that writing articles on blogging can help some of you get better or even inspired to get started on writing a blog.&lt;/p&gt;

&lt;p&gt;In this article, let's take a look at some common blogging mistakes that I observed beginners often make.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Thinking you are a bad writer
&lt;/h3&gt;

&lt;p&gt;This is probably the most common reasons why some developers don't want to start a blog. They doubt their writing skills.&lt;/p&gt;

&lt;p&gt;If you have read my article on &lt;a href="https://lo-victoria.com/why-you-dont-have-to-be-good-at-english-to-start-a-tech-blog" rel="noopener noreferrer"&gt;Why You Don't Have to be Good at English to Start a Tech Blog&lt;/a&gt;, you will know that not all dev-bloggers are that great at English too.&lt;/p&gt;

&lt;p&gt;As one of my favourite quote by Zig Zaglar goes,&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"You don't have to be great to start, but you have to start to be great."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We are still learning as we write on our blogs. And learning how to explain things in writing is always a lifelong lesson. There's always room for improvement. So don't make the mistake of giving up too early or not even starting just because you think you are a bad writer.&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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1608173481086%2FZNshzSjqY.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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1608173481086%2FZNshzSjqY.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Focusing too much on numbers
&lt;/h3&gt;

&lt;p&gt;A few of you have personally reached out to me and asked me how many articles should you publish a week. My answer to that is always...&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"It depends."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Because every blog is not the same. Most beginners are concerned with numbers: &lt;strong&gt;how many&lt;/strong&gt; articles to write per week, &lt;strong&gt;how many&lt;/strong&gt; views to get per article, &lt;strong&gt;how many&lt;/strong&gt; hours to spend on each article, etc.&lt;/p&gt;

&lt;p&gt;Focusing too much on numbers in the beginning of your writing journey can quickly drain your motivation, especially if the number doesn't meet your expectations. Instead, focus on &lt;strong&gt;building good writing habits&lt;/strong&gt; first and then naturally, you can figure out the numbers.&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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1608173455478%2FPPPFamDne.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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1608173455478%2FPPPFamDne.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Not really understanding your big WHY
&lt;/h3&gt;

&lt;p&gt;It takes a lot of time and commitment to start a blog. Starting one without really understanding &lt;strong&gt;why&lt;/strong&gt; you want to write can keep you from maintaining your blog in the long run.&lt;/p&gt;

&lt;p&gt;After all, having a successful blog may be a rewarding experience but it is definitely not easy and may not be for everyone. So as a beginner, knowing your WHY can achieve 2 things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Figure out whether blogging is really for you&lt;/li&gt;
&lt;li&gt;Figure out what you want to achieve through blogging&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I absolutely love the way Anita Ihuman summarizes what successful blogging means to her in &lt;a href="https://movi.hashnode.dev/what-successfully-blogging-means-to-me-ckhjbpjdr01l17ys14gqugn1h" rel="noopener noreferrer"&gt;this article&lt;/a&gt;. I agree with her definition and her article really resonates with me. Please check it out!&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Not being open to constructive feedback
&lt;/h3&gt;

&lt;p&gt;Whether one is a beginner or not, it is always helpful to listen to feedback. Not only will you improve, but you will also be able to find an audience/niche based on the feedback you receive.&lt;/p&gt;

&lt;p&gt;If you have not received feedback, it's a good idea to reach out to some amazing bloggers on Hashnode who will be more than happy to share some advice with you.&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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1608173561736%2FrVHtx-HGx.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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1608173561736%2FrVHtx-HGx.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Not Identifying Bottlenecks
&lt;/h3&gt;

&lt;p&gt;This was personally my biggest mistake when I started my blogging journey. Spending too much time brainstorming ideas or editing your drafts or cover images - basically "bottlenecking" the writing process, is a huge mistake.&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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1608173641664%2FotgfvA18U.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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1608173641664%2FotgfvA18U.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A typical technical article should take about 2-4 hours on average. If you spent a lot more time, ask yourself:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;At which aspect of the article is time most spent? Research? Writing?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For example, when I was starting out, I remembered my bottleneck was editing my drafts. I wrote a lot of drafts, but they never got published because I spent too much time continuously editing them to be "perfect". Once I've identified that this was an issue, I looked for grammar tools to help me speed up this step.&lt;/p&gt;

&lt;p&gt;If you can identify which step in the writing process that you spent unnecessarily so much time on, you can make some effort to improve on that. Therefore, becoming more efficient to write and publish articles. &lt;/p&gt;

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

&lt;p&gt;And that's all for now! Thank you for taking the time to read this article. If it is helpful, please like and share it around. Also, feel free to share any blogging mistakes you've made when starting a blog in the comments below. That way, we can learn from each other. Thanks and cheers!&lt;/p&gt;

</description>
      <category>developer</category>
      <category>generaladvice</category>
      <category>blogging</category>
      <category>developerblogging</category>
    </item>
    <item>
      <title>The Super Mario Effect for Developers</title>
      <dc:creator>Victoria Lo</dc:creator>
      <pubDate>Mon, 14 Dec 2020 07:47:34 +0000</pubDate>
      <link>https://dev.to/lo_victoria2666/the-super-mario-effect-for-developers-ael</link>
      <guid>https://dev.to/lo_victoria2666/the-super-mario-effect-for-developers-ael</guid>
      <description>&lt;p&gt;Over the weekend, I was watching some TED talk videos when I come across &lt;a href="https://www.youtube.com/watch?v=9vJRopau0g0"&gt;this talk&lt;/a&gt; by Mark Rober on the Super Mario Effect.&lt;/p&gt;

&lt;p&gt;It was such a fascinating concept to me - one that I have never actually thought about but is ever-present in my daily life. And so, in this article, I would like to share some things I've learned from researching about this topic and how it can be applied to developers for self-improvement.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is it?
&lt;/h2&gt;

&lt;p&gt;The Super Mario Effect is a learning concept that takes inspiration from the game Super Mario. If you've played the game, you probably remembered that the goal of the game is to get through all the levels to save Princess Peach from Bowser.&lt;/p&gt;

&lt;p&gt;The challenging part is that every level is teeming with pitfalls and obstacles but do you give up every time Mario falls down or fails to reach the end of the level? The answer is probably no. You will keep trying and as you fail each time, you will gradually learn how to conquer the level.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1IvKSNvg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1607135470053/legrbgieR.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1IvKSNvg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1607135470053/legrbgieR.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This attitude towards learning and mastery is called the Super Mario Effect - where the focus is on the process to reach goals (i.e. saving Princess Peach), not the failures (i.e. falling down pitfalls).&lt;/p&gt;

&lt;h2&gt;
  
  
  Capitalizing on Mario: Growth Mindset
&lt;/h2&gt;

&lt;p&gt;The Super Mario Effect can be applied to generally anyone, especially developers to improve your learning and motivation. The first is about having a &lt;strong&gt;growth mindset&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--VLxBSWt1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1607135512745/Z6KcvrSIi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VLxBSWt1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1607135512745/Z6KcvrSIi.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you never heard of the growth mindset, these are 2 characteristics of people with this mindset:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Does not fear failures
&lt;/h3&gt;

&lt;p&gt;When you were 3 years old and learning to walk, you probably fall down more than a dozen times. But that won't stop you from getting back up to try again. &lt;/p&gt;

&lt;p&gt;As we grow older, however, some people began to shift into the &lt;strong&gt;fixed mindset&lt;/strong&gt;. These people see failure as a sign to give up. They think obstacles cannot be overcome. They look for ways to avoid failures.&lt;/p&gt;

&lt;p&gt;The Super Mario effect emphasizes the "learn like a toddler" aka growth mindset. You collect experiences and learn from your mistakes without punishing yourself for it. Failure is not an obstacle, but an opportunity to learn. &lt;/p&gt;

&lt;h3&gt;
  
  
  2. Driven by Curiosity
&lt;/h3&gt;

&lt;p&gt;Another characteristic of the growth mindset is that you are driven by curiosity. Just like a toddler, you observe your surroundings a lot, you are inspired by others, you try to learn from others and you are curious about everything.&lt;/p&gt;

&lt;p&gt;Nothing is impossible to learn for those with a growth mindset. As a developer, being curious is an important asset. It keeps you motivated and less likely to feel burnt out. You don't just learn because you &lt;em&gt;have&lt;/em&gt; to, but because you &lt;em&gt;love&lt;/em&gt; to. Excellent developers are often self-driven and self-motivated because they adopt this attitude towards learning.&lt;/p&gt;

&lt;h2&gt;
  
  
  Capitalizing on Mario: Consistent Habits
&lt;/h2&gt;

&lt;p&gt;Just like how you will repeatedly challenge Super Mario to get to the next level, the Super Mario effect can inspire one to persevere and build good habits. How do we build such habits?&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Process First then Goals
&lt;/h3&gt;

&lt;p&gt;Author of Atomic Habits, James Clear, once mentioned that we often set goals first before thinking about the process. For example, let's say you want to be a writer, you would aim to be the best-selling author. But before that, you should fall in love with the &lt;strong&gt;process&lt;/strong&gt; of writing. Another example: if you aim to be a great developer, you should first fall in love with the joy of building and learning.&lt;/p&gt;

&lt;p&gt;Fall in love with the process, the practice, the repetitive tasks and that becomes a habit to achieve any goals.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"We are what we repeatedly do. Excellence is therefore, not an act but a habit." - Aristotle&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  2. Start with a Routine
&lt;/h3&gt;

&lt;p&gt;Having a routine can help make habits stick, and stick for long. The best way to start a routine to begin with something so easy that you can't say no. &lt;/p&gt;

&lt;p&gt;For example, if you want to start exercising every day, begin your routine by drinking water. It's so easy that you can do it and you will not make excuses to not do it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---Wzn7qHR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1607168231199/pUfy_yP3P.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---Wzn7qHR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1607168231199/pUfy_yP3P.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This way, you will start developing consistent habits and stay persistent when facing pitfalls just like Mario.&lt;/p&gt;

&lt;h2&gt;
  
  
  That's all for today!
&lt;/h2&gt;

&lt;p&gt;Thanks for reading this short article! I hope it's quite insightful. Be sure to like and share your thoughts in the comments below on how you can stay focused and motivated as a developer. If you are interested, please check out Atomic Habits by James Clear and the &lt;a href="https://www.youtube.com/watch?v=9vJRopau0g0"&gt;TED talk video&lt;/a&gt; by Mark Rober. Cheers!&lt;/p&gt;

&lt;h4&gt;
  
  
  Images from
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.gamespresso.com/wp-content/uploads/2016/03/46505-new_super_mario_bros-_upsyfer-3-1.jpg"&gt;https://www.gamespresso.com/wp-content/uploads/2016/03/46505-new_super_mario_bros-_upsyfer-3-1.jpg&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.techtello.com/wp-content/uploads/2020/06/fixed-mindset-vs-growth-mindset-chart.png"&gt;https://www.techtello.com/wp-content/uploads/2020/06/fixed-mindset-vs-growth-mindset-chart.png&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>generaladvice</category>
      <category>developer</category>
      <category>productivity</category>
      <category>todayilearned</category>
    </item>
    <item>
      <title>Beginner's Guide to Write and Publish React NPM Package</title>
      <dc:creator>Victoria Lo</dc:creator>
      <pubDate>Thu, 10 Dec 2020 02:57:15 +0000</pubDate>
      <link>https://dev.to/lo_victoria2666/beginner-s-guide-to-write-and-publish-react-npm-package-3on5</link>
      <guid>https://dev.to/lo_victoria2666/beginner-s-guide-to-write-and-publish-react-npm-package-3on5</guid>
      <description>&lt;p&gt;Hello everyone! In this article, we will learn step-by-step how to publish a React component as a npm package then set up a pipeline to automate testing and future publishes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Before we start...
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Make sure you have an npm account. If not, create one &lt;a href="https://www.npmjs.com/"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Make sure you have Node and npm installed on your machine. If not, get it &lt;a href="https://nodejs.org/en/download/"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Basic understanding of React will be needed.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Our simple package
&lt;/h2&gt;

&lt;p&gt;For this tutorial, we are making a simple React component that can immediately draw a specified polygon in any specified color.&lt;/p&gt;

&lt;p&gt;We will then publish this component as a npm package for anyone to install and use. Let's get started!&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Create React App
&lt;/h3&gt;

&lt;p&gt;First, let's create a new React app with the command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx create-react-app polygon-maker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Polygon.js
&lt;/h3&gt;

&lt;p&gt;Next, create a &lt;code&gt;Polygon.js&lt;/code&gt; file which will be the component we will publish as a package.&lt;/p&gt;

&lt;p&gt;The component will accept &lt;code&gt;props&lt;/code&gt; with properties such as &lt;code&gt;color&lt;/code&gt; and &lt;code&gt;shape&lt;/code&gt; to determine the type of polygon and its color.&lt;/p&gt;

&lt;p&gt;In this example, I have created a simple &lt;code&gt;square&lt;/code&gt;, which will be the returned &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; style attribute if &lt;code&gt;props.shape&lt;/code&gt; is square.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function Polygon(props) {
    const square = {
      background: props.color,
      width: "100px",
      height: "100px"
    };

   return (
      &amp;lt;div style={eval(props.shape)}&amp;gt;
      &amp;lt;/div&amp;gt;
    );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: App.js
&lt;/h3&gt;

&lt;p&gt;In &lt;code&gt;App.js&lt;/code&gt;, we can import Polygon at the top like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import Polygon from "./Polygon";
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And in the return function, simply add the Polygon component. Pass in &lt;code&gt;shape&lt;/code&gt; and &lt;code&gt;color&lt;/code&gt; as props.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div style={{display:"flex", backgroundColor:"black"}}&amp;gt;
      &amp;lt;Polygon shape="square" color="red"/&amp;gt;
&amp;lt;div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the square is rendered on the browser!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2fVLn2o---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1605591091774/Wh69YaWAY.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2fVLn2o---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1605591091774/Wh69YaWAY.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Add more shapes
&lt;/h3&gt;

&lt;p&gt;You can add more shapes in &lt;code&gt;Polygon.js&lt;/code&gt; so you can easily draw these shapes using this component. I made more such as rectangle, triangles, trapezoids, etc. Now I can pass them as &lt;code&gt;shape&lt;/code&gt; in the Polygon component in &lt;code&gt;App.js&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;App.js&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;&amp;lt;div style={{display:"flex", backgroundColor:"black"}}&amp;gt;
      &amp;lt;Polygon shape="square" color = "red"/&amp;gt;
      &amp;lt;Polygon shape="rectangle" color = "coral"/&amp;gt;
      &amp;lt;Polygon shape="oval" color = "yellow"/&amp;gt;
      &amp;lt;Polygon shape="triangleUp" color = "green"/&amp;gt;
      &amp;lt;Polygon shape="triangleDown" color = "blue"/&amp;gt;
      &amp;lt;Polygon shape="trapezoid" color = "purple"/&amp;gt;
      &amp;lt;Polygon shape="parallelogram" color = "pink"/&amp;gt;
 &amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the browser:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Drbdtpft--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1605591190285/cmyr5LwO6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Drbdtpft--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1605591190285/cmyr5LwO6.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Preparing to Publish
&lt;/h3&gt;

&lt;p&gt;We've finished making this simple component. Let's now publish it to npm! &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install babel to help transpile ES6 JavaScript.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install --save-dev @babel/cli @babel/preset-react
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Delete the &lt;code&gt;public&lt;/code&gt; folder.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Go to &lt;code&gt;package.json&lt;/code&gt; file. Under &lt;code&gt;scripts&lt;/code&gt;, add the line:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"publish": "rm -rf dist &amp;amp;&amp;amp; mkdir dist &amp;amp;&amp;amp;  babel src/Polygon.js -d dist --copy-files"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This script will make a directory called &lt;code&gt;dist&lt;/code&gt; and copy the compiled Polygon file inside it.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Still in &lt;code&gt;package.json&lt;/code&gt;, set the &lt;code&gt;private&lt;/code&gt; property to false and add the following:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"babel": {
    "presets": [
      "@babel/preset-react"
    ]
 }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Great! Now we are ready to publish this as a npm package.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6: Publish to npm
&lt;/h3&gt;

&lt;p&gt;In your command prompt/terminal, login to your npm account with:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Then, run our &lt;code&gt;publish&lt;/code&gt; script with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm run publish
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see a &lt;code&gt;dist&lt;/code&gt; directory appear in your root folder with &lt;code&gt;Polygon.js&lt;/code&gt; inside it. Rename that file to &lt;code&gt;index.js&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Finally, run:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;And our &lt;a href="https://www.npmjs.com/package/polygon-maker"&gt;Polygon maker&lt;/a&gt; package should be live on npm!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--eu3cpetm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1605759517296/z0IDGd_4j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--eu3cpetm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1605759517296/z0IDGd_4j.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Next Steps: Automating Tests and Updates
&lt;/h2&gt;

&lt;p&gt;After publishing your npm package, it is a good idea to set up a pipeline to automate future tests and version updates. &lt;a href="https://buddy.works/?utm_source=hashnode&amp;amp;utm_medium=referral&amp;amp;utm_campaign=content_victoria_lo&amp;amp;utm_content=react_npm"&gt;Buddy CI/CD&lt;/a&gt; is an intuitive tool that we can use to achieve this easily.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Add a Pipeline
&lt;/h3&gt;

&lt;p&gt;To get started, simply create an account at &lt;a href="https://buddy.works/?utm_source=hashnode&amp;amp;utm_medium=referral&amp;amp;utm_campaign=content_victoria_lo&amp;amp;utm_content=react_npm"&gt;buddy.works&lt;/a&gt;, and add your project by choosing the git provider you use.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kXjgKkrM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1605736561545/6vfEGgyNK.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kXjgKkrM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1605736561545/6vfEGgyNK.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After adding your project to Buddy, you can click &lt;strong&gt;'Add a new pipeline'&lt;/strong&gt; to set up a pipeline. Set &lt;code&gt;Trigger mode&lt;/code&gt; to &lt;strong&gt;On Push&lt;/strong&gt; and the branch as &lt;strong&gt;master&lt;/strong&gt; to ensure that the pipeline will automatically run when we made changes to our project.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ONGPVvuA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1605736813515/a2a2wnhK4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ONGPVvuA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1605736813515/a2a2wnhK4.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Build and run tests
&lt;/h3&gt;

&lt;p&gt;Next, add a &lt;strong&gt;Node action&lt;/strong&gt; into the pipeline. In the terminal, the following commands will build and run tests to make sure your npm package is error-free.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--084CZ-4V--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1605737811502/oYNZKxxyI.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--084CZ-4V--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1605737811502/oYNZKxxyI.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Authenticate and publish
&lt;/h3&gt;

&lt;p&gt;After we test our package, we can publish it to npm. To do that, we will need to login to our npm account.&lt;/p&gt;

&lt;p&gt;Add a &lt;strong&gt;Node action&lt;/strong&gt; with the following commands in the terminal:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nAqvrToO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1605737773858/Fu7RRNOwp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nAqvrToO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1605737773858/Fu7RRNOwp.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This command will allow us to authenticate ourselves using the &lt;code&gt;npm-cli-login&lt;/code&gt; package and environment variables such as &lt;code&gt;NPM_USER&lt;/code&gt;, &lt;code&gt;NPM_PASSWORD&lt;/code&gt; and &lt;code&gt;NPM_EMAIL&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To install &lt;code&gt;npm-cli-login&lt;/code&gt; and then run &lt;code&gt;npm publish&lt;/code&gt;, click on the &lt;strong&gt;Environment&lt;/strong&gt; tab and add this line in the terminal as shown in the image below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Aud8BQuf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1605737122838/4SUngMJ4g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Aud8BQuf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1605737122838/4SUngMJ4g.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we need to specify our environment variables by heading over to the &lt;strong&gt;Variables&lt;/strong&gt; tab and add the 3 variables as appropriate.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NkWNVcOt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1605738355366/6_rDFqlQT.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NkWNVcOt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1605738355366/6_rDFqlQT.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Add Email Notification
&lt;/h3&gt;

&lt;p&gt;Last but not least, we add an &lt;strong&gt;Email action&lt;/strong&gt; into our pipeline to notify us when the package is successfully tested and published to npm.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Ft9FiX8t--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1605737646220/qTjeqLzTW.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ft9FiX8t--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1605737646220/qTjeqLzTW.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Run Pipeline!
&lt;/h3&gt;

&lt;p&gt;Let's try a test run. As seen in the image below, the execution was successful and we have created an automated pipeline to test and publish any updates to our npm package.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--m63Y2jtq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1605738485135/xgg2P8ARL.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--m63Y2jtq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1605738485135/xgg2P8ARL.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;And that is how you can build and publish a simple React component to npm. After that, you can simply automate testing and future updates using a CI/CD tool like &lt;a href="https://buddy.works/?utm_source=hashnode&amp;amp;utm_medium=referral&amp;amp;utm_campaign=content_victoria_lo&amp;amp;utm_content=react_npm"&gt;Buddy&lt;/a&gt; (you can even configure a pipeline to &lt;a href="https://buddy.works/guides/reactjs-zero-downtime-deployment?utm_source=hashnode&amp;amp;utm_medium=referral&amp;amp;utm_campaign=content_victoria_lo&amp;amp;utm_content=react_npm"&gt;deploy React with zero-downtime!&lt;/a&gt;). &lt;/p&gt;

&lt;p&gt;Thanks for reading. I hope this article has been helpful. Please like and share it around if it is. Feel free to share any thoughts in the comments below. Till next time, cheers!&lt;/p&gt;

</description>
      <category>npm</category>
      <category>react</category>
      <category>beginners</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Tips to Negotiating Pay as a Freelance Developer</title>
      <dc:creator>Victoria Lo</dc:creator>
      <pubDate>Mon, 07 Dec 2020 10:03:50 +0000</pubDate>
      <link>https://dev.to/lo_victoria2666/tips-to-negotiating-pay-as-a-freelance-developer-52bg</link>
      <guid>https://dev.to/lo_victoria2666/tips-to-negotiating-pay-as-a-freelance-developer-52bg</guid>
      <description>&lt;p&gt;In the world of freelancing, pricing your services is probably one of the most debatable areas you will have to face. Ensuring that your proposed price is equivalent to your client's perceived value of you is necessary in order to reach an agreement.&lt;/p&gt;

&lt;p&gt;Hello everyone! As seen from the title, this article will cover a topic that I've never written on my blog before - freelancing. I decided to write on this topic due to requests by some of my readers.&lt;/p&gt;

&lt;h3&gt;
  
  
  My Experience with Freelancing
&lt;/h3&gt;

&lt;p&gt;I have been full-time freelancing as a web developer and technical writer since May 2020. With that said, all the tips I am about to give in this article merely comes from personal experience since I am no expert.&lt;/p&gt;

&lt;p&gt;In a span of 6 months, I have negotiated countless rates and have succeeded thanks to these tips I am about to share with you. So, let's get on with those tips!&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Talk about the value not the price
&lt;/h2&gt;

&lt;p&gt;When negotiating your price, it seems obvious to start getting into numbers and debating whether your services deserve $100/hour or $200/hour or etc.&lt;/p&gt;

&lt;p&gt;This, however, is the least effective way to negotiate. After all, when you are freelancing, numbers are all subjective. It doesn't mean anything unless you put some value into it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xN3x0Og4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1607335264581/F_guS5z19.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xN3x0Og4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1607335264581/F_guS5z19.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What I found most efficient in negotiation is to start with pitching your value. Try to answer the following questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why should &lt;code&gt;client_name&lt;/code&gt; hire you?&lt;/li&gt;
&lt;li&gt;How are you different from other developers offering the same services?&lt;/li&gt;
&lt;li&gt;What can you bring to the table that will help &lt;code&gt;client_name&lt;/code&gt; grow their business?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you understand your worth and value you can bring to your client, it will be easier for you to negotiate a higher rate, because you believe that you deserve it. Your perceived value will naturally be reflected in your confidence and the way you communicate to your client. The client will sense this and will be more willing to offer a higher price.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Numbers as previous evidences
&lt;/h2&gt;

&lt;p&gt;After determining your value and communicating that to the client, the next step is to use numbers to further persuade the client that your value is worth $x/hour or $x/word.&lt;/p&gt;

&lt;p&gt;These numbers include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The rate on your previous clients&lt;/li&gt;
&lt;li&gt;Number of projects you have done in the past&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Having a solid number allows the client to properly gauge your value. For example, the rate you charged previously was $150/hour and you have completed 3 projects so far. This will tell the client your level of experience, and then they will likely be more willing to match your previous price or offer your proposed higher price based on their perceived value of you.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Leverage benchmarks/other offers
&lt;/h2&gt;

&lt;p&gt;Including the market rate of similar freelancers also helps because it lets the client have a benchmark reference and get a rough idea of the pricing in the industry. It is especially useful if the client has not engaged in freelancing services before.&lt;/p&gt;

&lt;p&gt;If you are swamped with other offers, I think it is fair to be transparent and let them know that you are charging $x/hour for another client. That way, the client will more likely accept your price since another client has accepted it too.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Use the "We" language
&lt;/h2&gt;

&lt;p&gt;It is important to communicate to your client that you both are on the same side. Instead of saying,&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I need $x/hour because for me..."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I often say,&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"If it's okay, we can agree on $x/hour. If not, we can work something out."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That way, the client will not feel that he/she has to sacrifice or compromise to get your services. It is more about working together to reach an agreement that both parties will be happy with. A win-win and not a win-lose situation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LAl-OwNr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1607335302305/vpW3F6xem.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LAl-OwNr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1607335302305/vpW3F6xem.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Be polite but assertive
&lt;/h2&gt;

&lt;p&gt;Freelancers are often underpaid because they are afraid they may lose a precious client if they price too high. But my rule is, if you think the price is reasonable and reflects your value appropriately, then don't be afraid to be assertive.&lt;/p&gt;

&lt;p&gt;Of course, remember to always be polite because no client wants a freelancer that seems too stubborn and insisting. Simply state the aforementioned points 1 to 4 of this  article. Emphasizing on your value and experience, utilizing benchmarks and the "We" language to increase the client's perceived value of you.&lt;/p&gt;

&lt;p&gt;This won't usually happen but if the client still insist on their lower price, then it will be up to you. Compromise and accept the offer, or reject and find another client. Ultimately, negotiation is all about your perceived value of each other.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--IMomkmCI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1607335402191/JJ62owc-B.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IMomkmCI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1607335402191/JJ62owc-B.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;From my experience, most clients I have negotiated with gave positive responses and were highly satisfied with the value I bring. Although negotiating seemed scary to me at first, I realized that at the end of the day, it's about understanding each other and communication.&lt;/p&gt;

&lt;p&gt;I hope this has been a helpful read. If you have other negotiation tips, please share in the comments below. Don't forget to give a like or share if this is helpful. Thanks and cheers!&lt;/p&gt;




&lt;p&gt;&lt;em&gt;P.S. : I'm no longer freelancing but it has been a wonderful experience and I have definitely grown a lot as a developer and communicator since I started :) All the best to freelancers out there!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>freelancing</category>
      <category>developer</category>
      <category>personal</category>
      <category>tips</category>
    </item>
    <item>
      <title>Build Beautiful Documentation Websites with Docusaurus</title>
      <dc:creator>Victoria Lo</dc:creator>
      <pubDate>Fri, 04 Dec 2020 00:06:43 +0000</pubDate>
      <link>https://dev.to/lo_victoria2666/build-beautiful-documentation-websites-with-docusaurus-8o2</link>
      <guid>https://dev.to/lo_victoria2666/build-beautiful-documentation-websites-with-docusaurus-8o2</guid>
      <description>&lt;p&gt;A great documentation website says a lot about your software. It tells users how they can use the software. It tells developers what the software can do and how it is developed. In short, it is a must-have for developers.&lt;/p&gt;

&lt;p&gt;But making detailed and excellent documentation websites takes too much time and effort, especially because we want to focus our attention on development instead of writing documentation.&lt;/p&gt;

&lt;p&gt;In this article, I'd like to introduce &lt;a href="https://v2.docusaurus.io/"&gt;Docusaurus&lt;/a&gt; by Facebook, an easy-to-use documentation site generator powered by React and uses Markdown to write docs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Docusaurus: A Simple yet Elegant Solution
&lt;/h2&gt;

&lt;p&gt;Docusaurus is an open-source project that allows you to write documentation as Markdown and gives you tons of flexibility to customize and create visually appealing documentation websites in no time!&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Get Started
&lt;/h3&gt;

&lt;p&gt;Create a new documentation site by running this command:&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="nx"&gt;npx&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;docusaurus&lt;/span&gt;&lt;span class="sr"&gt;/init@latest latest &lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;name&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt; classi&lt;/span&gt;&lt;span class="err"&gt;c
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Head to the root folder and run &lt;code&gt;npm start&lt;/code&gt;. Your beautiful brand new site has been generated successfully.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9Wid6gkm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603072046894/8TzfiPsM5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9Wid6gkm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603072046894/8TzfiPsM5.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The classic template will generate the following files in the root directory:&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="nx"&gt;my&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;website&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;blog&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="mi"&gt;2019&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;05&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;28&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;hola&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;md&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="mi"&gt;2019&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;05&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;29&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;hello&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;world&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;md&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="mi"&gt;2020&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;05&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;welcome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;md&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;docs&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;doc1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;md&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;doc2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;md&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;doc3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;md&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="nx"&gt;mdx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;md&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;css&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="nx"&gt;custom&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;css&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="nx"&gt;pages&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;       &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;css&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;       &lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;js&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="nx"&gt;img&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;docusaurus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;js&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="kr"&gt;package&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;README&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;md&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;sidebars&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;js&lt;/span&gt;
&lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="nx"&gt;yarn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lock&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It might look quite intimidating for beginners so let's discuss how you can customize and create your own documentation website easily with this template.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Sidebar customization
&lt;/h3&gt;

&lt;p&gt;You can easily customize the documentation sidebar for your own documentation.&lt;/p&gt;

&lt;p&gt;The example sidebar looks like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Op9zNmgP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603133751592/Tpc3fG7Rc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Op9zNmgP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603133751592/Tpc3fG7Rc.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can customize this sidebar by simply updating the values in &lt;code&gt;sidebar.js&lt;/code&gt;, where the default sidebar is &lt;code&gt;someSidebar&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="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;someSidebar&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;Docusaurus&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;doc1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;doc2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;doc3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;Features&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mdx&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Docusaurus&lt;/code&gt; and &lt;code&gt;Features&lt;/code&gt; refer to the dropdown categories in the sidebar while the array of strings (i.e. doc1, doc2, doc3, mdx) are the markdown documentations found in the &lt;code&gt;docs&lt;/code&gt; folder of the site.&lt;/p&gt;

&lt;p&gt;For example, if I change the category names and move "doc3" to the category below instead.&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="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;someSidebar&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;Introduction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;doc1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;doc2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;About&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;doc3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mdx&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the sidebar would look like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4BMgeTb1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603133698817/oB35ldEX1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4BMgeTb1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603133698817/oB35ldEX1.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Configuration
&lt;/h3&gt;

&lt;p&gt;Changing the name and tagline of the template is probably the first thing to do when building your documentation website with Docusaurus.&lt;/p&gt;

&lt;p&gt;You can configure anything for your needs under &lt;code&gt;docusaurus.config.js&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Here's a snippet on things you can customize:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  title: 'My Site',
  tagline: 'The tagline of my site',
  url: 'https://your-docusaurus-test-site.com',
  baseUrl: '/',
  onBrokenLinks: 'throw',
  favicon: 'img/favicon.ico',
  organizationName: 'facebook', // Usually your GitHub org/user name.
  projectName: 'docusaurus', // Usually your repo name.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also customize the navigation bar and footer links in this file. Let's see how we can customize the navigation bar next.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. NavBar Customization
&lt;/h3&gt;

&lt;p&gt;By default, the navbar displays the site name, link to Docs and link to Blog. &lt;/p&gt;

&lt;p&gt;Let's say you want to create an About page and add the link to the page in the navbar. You can create an &lt;code&gt;about.md&lt;/code&gt; file in the &lt;code&gt;/pages&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;All Markdown documents must have an id so in &lt;code&gt;about.md&lt;/code&gt;, we can write specify its id and write a sample line :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;about&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;

&lt;span class="gu"&gt;## This is my About page.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, in &lt;code&gt;docusaurus.config.js&lt;/code&gt;, simply add this page id in the &lt;code&gt;navbar&lt;/code&gt; object, &lt;code&gt;items&lt;/code&gt; array.&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="nx"&gt;navbar&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nl"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
     &lt;span class="c1"&gt;//...&lt;/span&gt;
     &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;about&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;About&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;left&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="c1"&gt;//add this line&lt;/span&gt;
     &lt;span class="c1"&gt;//...&lt;/span&gt;
   &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, the rendered site will have your About page in the navbar.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PBO8rx3N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603137189339/oS7g46qr-.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PBO8rx3N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603137189339/oS7g46qr-.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Endless Possibilities to Build Beautiful Documentation
&lt;/h2&gt;

&lt;p&gt;Using Docusaurus, you can write your documentations and customize the template to your needs easily. Visit the &lt;a href="https://v2.docusaurus.io/"&gt;documentation website&lt;/a&gt; for more information.&lt;/p&gt;

&lt;p&gt;Some well-known documentation sites made with Docusaurus are &lt;a href="https://prettier.io/"&gt;Prettier&lt;/a&gt;, &lt;a href="https://reactnative.dev/"&gt;React Native&lt;/a&gt; and &lt;a href="https://mailgo.dev/docs/installation"&gt;mailgo&lt;/a&gt;. Feel free to look at &lt;a href="https://v2.docusaurus.io/showcase/"&gt;more&lt;/a&gt; to get inspiration on creating and designing your next documentation website.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Here is my documentation website for a project I made: &lt;a href="https://victoria-lo.github.io/Gameo/"&gt;https://victoria-lo.github.io/Gameo/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Thanks for reading! I hope it has been a helpful read! If it is, don't forget to like and share the article. Till next time, cheers!&lt;/p&gt;

</description>
      <category>developertools</category>
      <category>documentation</category>
      <category>beginners</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Let's Build a Simple Bulletin Board React App</title>
      <dc:creator>Victoria Lo</dc:creator>
      <pubDate>Sat, 28 Nov 2020 05:18:04 +0000</pubDate>
      <link>https://dev.to/lo_victoria2666/let-s-build-a-simple-bulletin-board-react-app-4gbm</link>
      <guid>https://dev.to/lo_victoria2666/let-s-build-a-simple-bulletin-board-react-app-4gbm</guid>
      <description>&lt;p&gt;In an article I wrote on &lt;a href="https://lo-victoria.com/making-draggable-components-in-react" rel="noopener noreferrer"&gt;Making Draggable Components in React&lt;/a&gt;, a reader requested to share how I built the &lt;a href="https://victoria-lo.github.io/bulletin-board/" rel="noopener noreferrer"&gt;Bulletin Board App&lt;/a&gt;, a simple demo app to showcase draggable components in React.&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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1598390335363%2Ftk8SnOrxD.gif%3Fauto%3Dformat%2Ccompress%26gif-q%3D60" 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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1598390335363%2Ftk8SnOrxD.gif%3Fauto%3Dformat%2Ccompress%26gif-q%3D60" alt="demo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this article, I'll be showing step-by-step how I made this simple app, perfect for React beginners to learn how to implement draggable components in their projects. Let's get started!&lt;/p&gt;

&lt;h2&gt;
  
  
  The App
&lt;/h2&gt;

&lt;p&gt;This app can do the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Allows user to type something in an input and press ENTER to generate a note of a random colour.&lt;/li&gt;
&lt;li&gt;Allows user to drag the note anywhere. Its position, colour and content will be saved even after the user exits the app.&lt;/li&gt;
&lt;li&gt;Allows the user to delete the note by clicking on the top-right 'X' button.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So let's build the app according to its 3 use cases.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Install &amp;amp; import packages
&lt;/h2&gt;

&lt;p&gt;Initialize a new Create React App by running the command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx create-react-app my-bulletin-board-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, in the project's root folder, install the following packages that we need:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;react-draggable&lt;/code&gt;: to implement draggable features for the notes&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;randomcolor&lt;/code&gt;: allow notes to be generated in random colours&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;uuid&lt;/code&gt;: generates a unique identifier for each note&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Install with the command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install react-draggable randomcolor uuid
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In &lt;code&gt;App.js&lt;/code&gt;, import the packages and React &lt;code&gt;useEffect&lt;/code&gt; and &lt;code&gt;useState&lt;/code&gt; hooks.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React, { useState, useEffect } from "react";
import "./App.css";
import Draggable from "react-draggable";
import { v4 as uuidv4 } from "uuid";
var randomColor = require("randomcolor");
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2: Initialize states
&lt;/h2&gt;

&lt;p&gt;We need to create and initialize 2 states using &lt;code&gt;useState&lt;/code&gt; hook.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;item&lt;/code&gt;: this is the value of the input field. Initialize as empty string.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;items&lt;/code&gt;: this is an array that contains all the notes generated, saved to localStorage. Initialize as empty array if localStorage has no saved &lt;code&gt;items&lt;/code&gt;.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const [item, setItem] = useState("");
const [items, setItems] = useState(
    JSON.parse(localStorage.getItem("items")) || []
  );
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3: Create input element
&lt;/h2&gt;

&lt;p&gt;We can create the HTML input and button elements in the return function of &lt;code&gt;App.js&lt;/code&gt; as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;input
    value={item}
    onChange={(e) =&amp;gt; setItem(e.target.value)}
    placeholder="Enter something..."
    onKeyPress={(e) =&amp;gt; keyPress(e)}
/&amp;gt;
&amp;lt;button onClick={newitem}&amp;gt;ENTER&amp;lt;/button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It looks something like:&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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1602973254330%2FMXgfERye0.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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1602973254330%2FMXgfERye0.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; has the following attributes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;value&lt;/code&gt;: set to the value of the &lt;code&gt;item&lt;/code&gt; state&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;onChange&lt;/code&gt;: update &lt;code&gt;item&lt;/code&gt; state every time there is a change in the input's value&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;placeholder&lt;/code&gt;: description when there's nothing in the input field&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;onKeyPress&lt;/code&gt;: calls the &lt;code&gt;keyPress&lt;/code&gt; function that checks if the key pressed is ENTER, call the &lt;code&gt;newitem&lt;/code&gt; function.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The keyPress function is written like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const keyPress = (event) =&amp;gt; {
    var code = event.keyCode || event.which;
    if (code === 13) {
      newitem();
    }
  };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This way, the user can type something in the input field then press ENTER key or click on the ENTER button to generate a new note on screen.&lt;/p&gt;

&lt;p&gt;As for the ENTER button, when the user clicks on it, the &lt;code&gt;newitem&lt;/code&gt; function will be called. Let's write this function next.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: newitem
&lt;/h2&gt;

&lt;p&gt;This function generates a new note on the screen with the string the user typed in input (i.e. &lt;code&gt;item&lt;/code&gt;). A note is an object with the following properties:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;id&lt;/code&gt;: unique identifier generated using uuidv4()&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;item&lt;/code&gt;: the string content of the note, which is the state &lt;code&gt;item&lt;/code&gt;'s value&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;color&lt;/code&gt;: the background colour of the note, generated with &lt;code&gt;randomColor({luminosity: "light",})&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;defaultPos&lt;/code&gt;: the x and y coordinates of the note. Initialized to &lt;code&gt;{x:100, y:0}&lt;/code&gt;.
&lt;/li&gt;
&lt;/ul&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;newitem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="c1"&gt;//if input is not blank, create a new item object&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newitem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;uuidv4&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="na"&gt;item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;randomColor&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;luminosity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;light&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,}),&lt;/span&gt;
        &lt;span class="na"&gt;defaultPos&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;};&lt;/span&gt;
      &lt;span class="c1"&gt;//add this new item object to the items array&lt;/span&gt;
      &lt;span class="nf"&gt;setItems&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;items&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;items&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newitem&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
      &lt;span class="c1"&gt;//reset item value to empty string&lt;/span&gt;
      &lt;span class="nf"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Enter a item&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, let's update our localStorage every time our &lt;code&gt;items&lt;/code&gt; array is updated. We can use the &lt;code&gt;useEffect&lt;/code&gt; hook to achieve this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;useEffect(() =&amp;gt; {
    localStorage.setItem("items", JSON.stringify(items));
  }, [items]);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we should display our note objects from our &lt;code&gt;items&lt;/code&gt; array on screen.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Display items
&lt;/h2&gt;

&lt;p&gt;In our return function, below our input and button element, we can display our notes using the &lt;code&gt;map&lt;/code&gt; array method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{items.map((item, index) =&amp;gt; {
        return (
          &amp;lt;Draggable
            key={item.id}
            defaultPosition={item.defaultPos}
            onStop={(e, data) =&amp;gt; {
              updatePos(data, index);
            }}
          &amp;gt;
            &amp;lt;div style={{ backgroundColor: item.color }} className="box"&amp;gt;
              {`${item.item}`}
              &amp;lt;button id="delete" onClick={(e) =&amp;gt; deleteNote(item.id)}&amp;gt;
                X
              &amp;lt;/button&amp;gt;
            &amp;lt;/div&amp;gt;
          &amp;lt;/Draggable&amp;gt;
        );
      })}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For every note object in the &lt;code&gt;items&lt;/code&gt; array, we will create a &lt;code&gt;&amp;lt;Draggable&amp;gt;&lt;/code&gt; component in which:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;key&lt;/code&gt; attribute =&lt;code&gt;id&lt;/code&gt; of the object. &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;defaultPosition&lt;/code&gt; of the component = &lt;code&gt;defaultPos&lt;/code&gt; of the object.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;onStop&lt;/code&gt;, which is when the user stops dragging the note on screen, will call the &lt;code&gt;updatePos&lt;/code&gt; function.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Inside the &lt;code&gt;&amp;lt;Draggable&amp;gt;&lt;/code&gt; component, we have the &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; that will render the note's &lt;code&gt;item&lt;/code&gt; property on a background with the note's &lt;code&gt;colour&lt;/code&gt; property. And finally, we have an 'X' button which calls the &lt;code&gt;deleteNote&lt;/code&gt; function upon clicked.&lt;/p&gt;

&lt;p&gt;Now, we should be able to generate a new randomly-coloured note on screen every time we type something in the input and press ENTER.&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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1602976761959%2FCw_l5DCGH.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1602976761959%2FCw_l5DCGH.gif" alt="gif1.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, if we dragged the note and reload the page, the note's position will not be saved because we haven't written our &lt;code&gt;updatePos&lt;/code&gt; function. Let's write that function next.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6: updatePos
&lt;/h2&gt;

&lt;p&gt;This function is called every time we stop dragging the note. That way, we can save the final position of the note to our &lt;code&gt;items&lt;/code&gt; array in localStorage. &lt;/p&gt;

&lt;p&gt;The next time we visit the page, the app will remember the last position of the note. It won't reset to &lt;code&gt;{x:&lt;/code&gt;100, y:0}` all the time.&lt;/p&gt;

&lt;p&gt;Here's how the function works:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We pass &lt;code&gt;data&lt;/code&gt; as the first parameter of the function. It contains the x and y coordinates of our note.&lt;/li&gt;
&lt;li&gt;Clone our &lt;code&gt;items&lt;/code&gt; array into a new array called &lt;code&gt;newArr&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Get the index of the note in the array we want to update from the 2nd parameter &lt;code&gt;index&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Set the new coordinate values of the note in its &lt;code&gt;defaultPos&lt;/code&gt; property.&lt;/li&gt;
&lt;li&gt;Set &lt;code&gt;items&lt;/code&gt; to the value of the &lt;code&gt;newArr&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;&lt;br&gt;
const updatePos = (data, index) =&amp;gt; {&lt;br&gt;
    let newArr = [...items];&lt;br&gt;
    newArr[index].defaultPos = { x: data.x, y: data.y };&lt;br&gt;
    setItems(newArr);&lt;br&gt;
 };&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;br&gt;
Great! Now the position of any note will be updated and saved to localStorage whenever it changes. Let's move on to the final function: &lt;code&gt;deleteNote&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 7: deleteNote
&lt;/h2&gt;

&lt;p&gt;In this function, the note will be deleted from both on screen and from the &lt;code&gt;items&lt;/code&gt; array in localStorage.&lt;/p&gt;

&lt;p&gt;This function is pretty straightforward. We can simply use the &lt;code&gt;filter&lt;/code&gt; array method to remove the note whose &lt;code&gt;id&lt;/code&gt; property matches the &lt;code&gt;id&lt;/code&gt; parameter of the function.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;javascript&lt;br&gt;
const deleteNote = (id) =&amp;gt; {&lt;br&gt;
    setItems(items.filter((item) =&amp;gt; item.id !== id));&lt;br&gt;
 };&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  And that's it!
&lt;/h2&gt;

&lt;p&gt;We should now have a simple working bulletin board app like 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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1602976645099%2FJc8Zq-ywp.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1602976645099%2FJc8Zq-ywp.gif" alt="gif2.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thanks for reading. I hope this is a helpful to implement React Draggable into your projects. For more details on React Draggable, feel free to check out my article &lt;a href="https://lo-victoria.com/making-draggable-components-in-react" rel="noopener noreferrer"&gt;Making Draggable Components in React&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Please like and share this article if it is helpful, and leave any questions in the comments below. Check out the &lt;a href="https://victoria-lo.github.io/bulletin-board/" rel="noopener noreferrer"&gt;demo&lt;/a&gt; or the &lt;a href="https://github.com/victoria-lo/bulletin-board/" rel="noopener noreferrer"&gt;repo&lt;/a&gt; of this app. For more information on the packages we used to build this app, feel free to read the section below. &lt;/p&gt;

&lt;p&gt;Special thanks to Manas Garg for requesting this article. Sorry it took so long to finally publish. Thanks and cheers!&lt;/p&gt;




&lt;h3&gt;
  
  
  See Also
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/STRML/react-draggable/" rel="noopener noreferrer"&gt;react-draggable&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/randomcolor" rel="noopener noreferrer"&gt;randomcolor&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/uuid" rel="noopener noreferrer"&gt;uuid&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>react</category>
      <category>reactdraggable</category>
      <category>javascript</category>
      <category>localstorage</category>
    </item>
    <item>
      <title>Introduction to Firebase Storage #2: Retrieve &amp; Delete Files</title>
      <dc:creator>Victoria Lo</dc:creator>
      <pubDate>Tue, 24 Nov 2020 04:55:42 +0000</pubDate>
      <link>https://dev.to/lo_victoria2666/introduction-to-firebase-storage-2-retrieve-delete-files-3bpj</link>
      <guid>https://dev.to/lo_victoria2666/introduction-to-firebase-storage-2-retrieve-delete-files-3bpj</guid>
      <description>&lt;p&gt;Hello everyone, in this article, we shall continue from where we left off in the previous article on &lt;a href="https://lo-victoria.com/introduction-to-firebase-storage-uploading-files"&gt;How to Upload Files to Firebase Cloud Storage&lt;/a&gt;. We will now learn how to retrieve and delete files from Firebase Cloud Storage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Retrieve files from Firebase
&lt;/h2&gt;

&lt;p&gt;Refer to the previous article to learn how to set up Firebase Cloud Storage and create our project that we will continue building in this article.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Create allImages state
&lt;/h3&gt;

&lt;p&gt;Initialize an array called &lt;code&gt;allImages&lt;/code&gt;. This array will hold all the image URLs retrieved from Firebase.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; const [allImages, setImages] = useState([]);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: getFromFirebase
&lt;/h3&gt;

&lt;p&gt;Let's create a function called &lt;code&gt;getFromFirebase&lt;/code&gt; that will handle retrieving all files from Firebase.&lt;/p&gt;

&lt;p&gt;In this function, we want to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1: Get reference to our storage bucket&lt;/li&gt;
&lt;li&gt;2: Use &lt;code&gt;listAll()&lt;/code&gt; to get all the reference object inside it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;listAll()&lt;/code&gt; returns the reference to the images, not the images themselves. It has 2 properties: &lt;code&gt;items&lt;/code&gt; and &lt;code&gt;prefixes&lt;/code&gt;. Items are the image reference whereas prefixes are folders, in case you have nested folders in storage.&lt;/p&gt;

&lt;p&gt;Below is an example of what &lt;code&gt;listAll()&lt;/code&gt; returns when I have 8 images in storage.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--K4MxuY6y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1600472345778/L5c5rQivc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--K4MxuY6y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1600472345778/L5c5rQivc.png" alt="listAll.PNG"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;3: Then loop through each &lt;code&gt;items&lt;/code&gt; reference with &lt;code&gt;forEach()&lt;/code&gt; to obtain the image URL with &lt;code&gt;getDownloadURL()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;4: Finally, add this URL into the &lt;code&gt;allImages&lt;/code&gt; array with &lt;code&gt;setImages()&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  const getFromFirebase = () =&amp;gt; {
    //1.
    let storageRef = storage.ref();
    //2.
    storageRef.listAll().then(function (res) {
        //3.
        res.items.forEach((imageRef) =&amp;gt; {
          imageRef.getDownloadURL().then((url) =&amp;gt; {
              //4.
              setImages((allImages) =&amp;gt; [...allImages, url]);
          });
        });
      })
      .catch(function (error) {
        console.log(error);
      });
  };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Step 3: Display Images
&lt;/h3&gt;

&lt;p&gt;We can then create a component where we can display our images from the URLs in the&lt;code&gt;allImages&lt;/code&gt; array.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; &amp;lt;div id="photos"&amp;gt;
     {allImages.map((image) =&amp;gt; {
        return (
           &amp;lt;div key={image} className="image"&amp;gt;
              &amp;lt;img src={image} alt="" /&amp;gt;
              &amp;lt;button onClick={() =&amp;gt; deleteFromFirebase(image)}&amp;gt;
               Delete
              &amp;lt;/button&amp;gt;
           &amp;lt;/div&amp;gt;
         );
     })}
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On each image, we can have a Delete button to allow users to delete the picture they clicked on. Let's look at how we can implement the &lt;code&gt;deletedFromFirebase()&lt;/code&gt; for the button.&lt;/p&gt;

&lt;h2&gt;
  
  
  Delete Files From Firebase
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 4: deleteFromFirebase
&lt;/h3&gt;

&lt;p&gt;Our &lt;code&gt;deleteFromFirebase&lt;/code&gt; function takes in our image URL as an argument and deletes that URL from Firebase.&lt;/p&gt;

&lt;p&gt;Here's how we can implement the function:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Using &lt;code&gt;refFromURL()&lt;/code&gt;, we can get the image reference from Firebase Storage of the image we want to delete.&lt;/li&gt;
&lt;li&gt;Then use &lt;code&gt;.delete()&lt;/code&gt; to delete the image from Firebase.&lt;/li&gt;
&lt;li&gt;Finally, we remove that URL from our &lt;code&gt;allImages&lt;/code&gt; array.
&lt;/li&gt;
&lt;/ol&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;deleteFromFirebase&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//1.&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;pictureRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;refFromURL&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="c1"&gt;//2.&lt;/span&gt;
    &lt;span class="nx"&gt;pictureRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;//3.&lt;/span&gt;
        &lt;span class="nx"&gt;setImages&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;allImages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;image&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;image&lt;/span&gt; &lt;span class="o"&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;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Picture is deleted successfully!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  And the result!
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xIji_qfb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1600459567977/VxCJlJfIr.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xIji_qfb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1600459567977/VxCJlJfIr.gif" alt="ezgif.com-video-to-gif.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And that's how we can upload images, retrieve and display them and delete them! &lt;/p&gt;

&lt;p&gt;To view the project I made for this tutorial, please visit the repo &lt;a href="https://github.com/victoria-lo/photo-album"&gt;here&lt;/a&gt;. And please read the &lt;a href="https://firebase.google.com/docs/storage"&gt;Firebase Documentation&lt;/a&gt; for more information. &lt;/p&gt;

&lt;p&gt;Thank you for reading. I hope it's been a helpful 2-part read to help you get started with using Firebase Cloud Storage. As always, please do not hesitate to share any thoughts in the comments below. Till next time, cheers!&lt;/p&gt;

</description>
      <category>firebase</category>
      <category>react</category>
      <category>storage</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Introduction to Firebase Storage #1: Uploading Files</title>
      <dc:creator>Victoria Lo</dc:creator>
      <pubDate>Sat, 21 Nov 2020 04:21:19 +0000</pubDate>
      <link>https://dev.to/lo_victoria2666/introduction-to-firebase-storage-1-uploading-files-ia5</link>
      <guid>https://dev.to/lo_victoria2666/introduction-to-firebase-storage-1-uploading-files-ia5</guid>
      <description>&lt;p&gt;Some apps allow users to upload images and files, read them, delete them and even download them whenever users want. Such functionality can be useful for social media platforms, blogging platforms or storage services. Firebase Cloud Storage offers a solution to store user-generated content easily. &lt;/p&gt;

&lt;p&gt;Hello fellow devs, in this article, we'll be discussing Firebase Cloud Storage and how we can implement it to upload, retrieve and store user files securely.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Firebase Cloud Storage?
&lt;/h2&gt;

&lt;p&gt;Firebase Cloud Storage is a service that developers can use to store and download files generated directly by clients. No server-side code is needed. &lt;/p&gt;

&lt;p&gt;It uses Google Cloud Storage buckets to store the files, allowing accessibility from both Google Cloud and Firebase. These buckets are formed within a hierarchical structure. For example:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--S58x-OyZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cloud.google.com/billing/docs/images/resource-hierarchy-overview.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--S58x-OyZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cloud.google.com/billing/docs/images/resource-hierarchy-overview.png" alt="structure"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What I really like about Firebase Cloud Storage is how seamless it integrates with Firebase Authentication so you can organize uploaded files based on each user and apply access controls if needed. &lt;/p&gt;

&lt;p&gt;Also, it scales automatically so there's no worry about moving to another provider when stored data gets too large.&lt;/p&gt;

&lt;p&gt;Now that we know what Firebase Storage can do, let's try using it in our project. For this tutorial, I'm making a simple photo album app that allows users to upload, view and delete images.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Create a new Firebase Project
&lt;/h2&gt;

&lt;p&gt;Head over to firebase.google.com and create a new project.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4PWSjuyl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1600303907780/KdWPwlQLu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4PWSjuyl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1600303907780/KdWPwlQLu.png" alt="1.PNG"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the dashboard, click on the Web icon to initialize Firebase for Web Apps.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_-9bzVVI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1600304103899/Dbm6A9Qfk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_-9bzVVI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1600304103899/Dbm6A9Qfk.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Follow the steps by Firebase and you'll reach a page that shows your config variables (see image below). This is important so copy and save it somewhere. We will use it soon.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8QM8_0OL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1600304118375/NriRpcYnd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8QM8_0OL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1600304118375/NriRpcYnd.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, head over to the &lt;strong&gt;Storage&lt;/strong&gt; tab and click on the &lt;strong&gt;'Get Started'&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Xy8IIHez--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1600304375295/1Pqiesw-C.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Xy8IIHez--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1600304375295/1Pqiesw-C.png" alt="2.PNG"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You'll see a pop-up window that asks if you are okay with some settings. Replace the &lt;code&gt;request.auth !=null&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt;. This ensures we are allowed to upload files to Firebase without needing authentication for the simplicity of this tutorial.&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;'Next'&lt;/strong&gt; to proceed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LLetRxje--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1600391465512/PwN8MPGBU.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LLetRxje--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1600391465512/PwN8MPGBU.png" alt="3.PNG"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And there you go! Firebase Cloud Storage is now enabled. Let's integrate it into our app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Create a React App
&lt;/h2&gt;

&lt;p&gt;For this example, I'm using a react project template. You can use whatever front-end framework you like.&lt;/p&gt;

&lt;p&gt;To create a React project, simply run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx create-react-app &amp;lt;app-name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once your project is created, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install firebase
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a package that contains the necessary tools and infrastructure we need to set up Firebase in our app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: config.js
&lt;/h2&gt;

&lt;p&gt;Create a file called &lt;code&gt;config.js&lt;/code&gt; to store our Firebase config variables that we copied earlier.&lt;/p&gt;

&lt;p&gt;Our &lt;code&gt;config.js&lt;/code&gt; will 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;import firebase from "firebase/app";
import "firebase/storage";

const app = firebase.initializeApp({
  apiKey: process.env.REACT_APP_API_KEY,
  authDomain: process.env.REACT_APP_AUTH_DOMAIN,
  databaseURL: process.env.REACT_APP_DATABASE_URL,
  projectId: process.env.REACT_APP_PROJECT_ID,
  storageBucket: process.env.REACT_APP_STORAGE_BUCKET,
  messagingSenderId: process.env.REACT_APP_MESSAGING_SENDER_ID,
});

// Get a reference to the storage service, export it for use
export const storage = firebase.storage(); 

export default app;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that I'm storing the actual config values in my &lt;code&gt;.env&lt;/code&gt; file and accessing them as &lt;code&gt;process.env.VARIABLE_NAME&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you are new with environment variables, I found this nice &lt;a href="https://dev.to/deammer/loading-environment-variables-in-js-apps-1p7p"&gt;article&lt;/a&gt; explaining how to use it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Step 4: Uploading Files to Storage
&lt;/h2&gt;

&lt;p&gt;In &lt;code&gt;App.js&lt;/code&gt;, we can import our storage reference that we exported from our &lt;code&gt;config.js&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import {storage} from "./config";
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In order to upload files, we need to have an input field for the user. We can create an input element like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;input type="file" accept="image/x-png,image/jpeg" /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By specifying the &lt;code&gt;type&lt;/code&gt; to file, the input field will be a file picker. &lt;/p&gt;

&lt;p&gt;For this example, we only accept files that are &lt;code&gt;.png&lt;/code&gt; or &lt;code&gt;.jpeg&lt;/code&gt;. We can specify this requirement in the &lt;code&gt;accept&lt;/code&gt; attribute.&lt;/p&gt;

&lt;p&gt;Now let's add a button that will upload our image to Firebase when clicked.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; &amp;lt;button&amp;gt;Upload to Firebase&amp;lt;/button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point, the UI should look something simple like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QcKdZWtV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1600306366223/ctBtN0K2B.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QcKdZWtV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1600306366223/ctBtN0K2B.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Create Image state
&lt;/h3&gt;

&lt;p&gt;To track whether our user has supplied a file in the input, we should have an &lt;code&gt;image&lt;/code&gt; state. First, import the &lt;code&gt;useState&lt;/code&gt; hook.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React, { useState } from "react";
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And initialize the state to null:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const [image, setImage] = useState(null);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. onImageChange
&lt;/h3&gt;

&lt;p&gt;Next, let's create an &lt;code&gt;onImageChange&lt;/code&gt; function which will update the &lt;code&gt;image&lt;/code&gt; state every time the user supplied a new file to the input field.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const onImageChange = (e) =&amp;gt; {
    const reader = new FileReader();
    let file = e.target.files[0]; // get the supplied file
    // if there is a file, set image to that file
    if (file) {
      reader.onload = () =&amp;gt; {
        if (reader.readyState === 2) {
          console.log(file);
          setImage(file);
        }
      };
      reader.readAsDataURL(e.target.files[0]);
    // if there is no file, set image back to null
    } else {
      setImage(null);
    }
  };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, we will pass this function into the &lt;code&gt;onChange&lt;/code&gt; handler of our input element.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;input type="file" accept="image/x-png,image/jpeg" onChange={(e) =&amp;gt; {onImageChange(e); }}/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. uploadToFirebase
&lt;/h3&gt;

&lt;p&gt;Now let's create an &lt;code&gt;uploadToFirebase&lt;/code&gt; function for our button so that the image will be uploaded to Firebase when the user clicks the button.&lt;/p&gt;

&lt;p&gt;This is how we can implement the function:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Check if the &lt;code&gt;image&lt;/code&gt; state is null. If it is, ask the user to supply a file first. &lt;/li&gt;
&lt;li&gt;If &lt;code&gt;image&lt;/code&gt; is a file, we'll create a root reference to our storage.&lt;/li&gt;
&lt;li&gt;Then we create a child reference to store our file. We can name that reference by the image's &lt;code&gt;name&lt;/code&gt; property.&lt;/li&gt;
&lt;li&gt;Finally, use &lt;code&gt;put(image)&lt;/code&gt; to store our file in the reference.&lt;/li&gt;
&lt;li&gt;Then have a callback function to let the user know that the file has been uploaded to Firebase successfully.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here's the implementation in code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  const uploadToFirebase = () =&amp;gt; {
    //1.
    if (image) {
      //2.
      const storageRef = storage.ref();
      //3.
      const imageRef = storageRef.child(image.name);
      //4.
      imageRef.put(image)
     //5.
     .then(() =&amp;gt; {
        alert("Image uploaded successfully to Firebase.");
    });
    } else {
      alert("Please upload an image first.");
    }
  };

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  That should do it!
&lt;/h3&gt;

&lt;p&gt;Let's check if it works.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6SK2PSCe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1600310168127/Fxc-kfLzb.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6SK2PSCe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1600310168127/Fxc-kfLzb.gif" alt="gif.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Yay it does! The uploaded image is in Firebase Storage.&lt;/p&gt;

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

&lt;p&gt;With Firebase Cloud Storage, there are many things you can do to handle, organize and store user's data and files. Stay tuned for the next article on how to retrieve, display and delete files from Firebase Storage.&lt;/p&gt;

&lt;p&gt;Thanks for reading and I hope it was helpful in any way. Feel free to ask any questions in the comments below and refer to the Firebase &lt;a href="https://firebase.google.com/docs/storage"&gt;documentation&lt;/a&gt; to read more about it yourself. Take care and cheers!&lt;/p&gt;

</description>
      <category>firebase</category>
      <category>react</category>
      <category>storage</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Testing React Components and Apps with Enzyme</title>
      <dc:creator>Victoria Lo</dc:creator>
      <pubDate>Tue, 10 Nov 2020 05:14:48 +0000</pubDate>
      <link>https://dev.to/lo_victoria2666/testing-react-components-and-apps-with-enzyme-fl9</link>
      <guid>https://dev.to/lo_victoria2666/testing-react-components-and-apps-with-enzyme-fl9</guid>
      <description>&lt;p&gt;Testing front-end applications can be a pain and time-consuming to write. In this article, let's discuss Enzyme and how we can use it to make testing UI much easier.&lt;/p&gt;

&lt;h2&gt;
  
  
  First, What's Enzyme?
&lt;/h2&gt;

&lt;p&gt;Enzyme is a JavaScript testing utility for React. It makes testing React components easy by asserting, manipulating, and traversing every components' output. Currently, it is open sourced and maintained by AirBnB.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--N1bn2JjW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1601698225471/gr5Os7Nhh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--N1bn2JjW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1601698225471/gr5Os7Nhh.png" alt="enzyme"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;In terms of testing, you've probably heard of Mocha and Chai. This JavaScript test framework and assertion library are often used cohesively to write unit and functional tests.&lt;/p&gt;

&lt;p&gt;But what about testing React components? Enzyme is the solution for the following reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Intuitive and flexible API&lt;/li&gt;
&lt;li&gt;Compatible with major and common test runners&lt;/li&gt;
&lt;li&gt;Supports shallow rendering, DOM rendering and static rendered markup&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WOZN5Yzj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dzone.com/storage/temp/7506322-enzyme-structure.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WOZN5Yzj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dzone.com/storage/temp/7506322-enzyme-structure.png" alt="renders"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Enzyme Implementation
&lt;/h2&gt;

&lt;p&gt;Now let's learn how we can use Enzyme with Jest, a test runner in React, to write tests.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Install Enzyme
&lt;/h3&gt;

&lt;p&gt;First we install the package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install enzyme enzyme-adapter-react-16
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: setupTests.js
&lt;/h3&gt;

&lt;p&gt;When you create a new React app, there should be a &lt;code&gt;setupTests.js&lt;/code&gt; file initialized by default. &lt;/p&gt;

&lt;p&gt;Add the following code in that file to import Enzyme:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { configure } from "enzyme";
import Adapter from "enzyme-adapter-react-16";
configure({ adapter: new Adapter() });

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: App.test.js
&lt;/h3&gt;

&lt;p&gt;In this example, let's test our &lt;code&gt;App.js&lt;/code&gt; file. Jest, our test runner, will run test files automatically if the file is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Any .js file in a folder named &lt;strong&gt;tests&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Any .js file with a name like [name].spec.js&lt;/li&gt;
&lt;li&gt;Any .js file with a name like [name].test.js&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So let's have an &lt;code&gt;App.test.js&lt;/code&gt; file to test our &lt;code&gt;App.js&lt;/code&gt;. In that file, we'll write some simple test like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React from "react";
import App from "./App";
import renderer from "react-test-renderer";
import { shallow } from "enzyme";

it("renders without crashing", () =&amp;gt; {
  shallow(&amp;lt;App /&amp;gt;);
});

it("renders correctly", () =&amp;gt; {
  const rendered = renderer.create(&amp;lt;App /&amp;gt;);
  expect(rendered.toJSON()).toMatchSnapshot();
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In our first test, we use shallow rendering to isolate an individual component such as &lt;code&gt;&amp;lt;App/&amp;gt;&lt;/code&gt; and make sure that it renders.&lt;/p&gt;

&lt;p&gt;Our next test is a snapshot test to test if the UI renders correctly. According to Jest documentation,&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A typical snapshot test case renders a UI component, takes a snapshot, then compares it to a reference snapshot file stored alongside the test. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Run Tests
&lt;/h3&gt;

&lt;p&gt;With these 2 simple example tests, let's run them with:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;If all tests passed, the terminal should show:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--r8-NIuN_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1601670328433/Y64ed1utK.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--r8-NIuN_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1601670328433/Y64ed1utK.png" alt="tests"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Automating your tests
&lt;/h2&gt;

&lt;p&gt;So now we know how to write simple UI tests with Enzyme. For more details, visit their &lt;a href="https://enzymejs.github.io/enzyme/"&gt;documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It's always good to keep your test automated instead of manually running &lt;code&gt;npm test&lt;/code&gt; every time. &lt;a href="https://buddy.works/?utm_source=hashnode&amp;amp;utm_medium=referral&amp;amp;utm_campaign=content_victoria_lo&amp;amp;utm_content=react_enzyme"&gt;Buddy CI/CD&lt;/a&gt; is a tool I use to automate tests easily, and it works well for React apps.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Add a Pipeline
&lt;/h3&gt;

&lt;p&gt;To get started, simply create an account at &lt;a href="https://buddy.works/?utm_source=hashnode&amp;amp;utm_medium=referral&amp;amp;utm_campaign=content_victoria_lo&amp;amp;utm_content=react_enzyme"&gt;buddy.works&lt;/a&gt;, and add your project by choosing the git provider you use.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--AwOyVpLf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1604431192875/79b3Xr_H3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AwOyVpLf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1604431192875/79b3Xr_H3.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then, add a pipeline in which we can automate our tests in our React app. Ensure that 'Trigger Mode' is set to &lt;strong&gt;On Push&lt;/strong&gt; and the branch as &lt;code&gt;master&lt;/code&gt; so that our test will automatically run every time we made changes to our project.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qxmZNYel--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1601688238676/uVJGtMCQX.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qxmZNYel--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1601688238676/uVJGtMCQX.png" alt="pipeline"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Add a Node action
&lt;/h3&gt;

&lt;p&gt;Since we're using the command &lt;code&gt;npm test&lt;/code&gt; to run our test, we can add a &lt;strong&gt;Node&lt;/strong&gt; action to our pipeline.&lt;/p&gt;

&lt;p&gt;In our action, we run the commands to install our package and run our test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install
npm test
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--h6Xl9ab0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1601681676813/We9EHYIZY.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--h6Xl9ab0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1601681676813/We9EHYIZY.png" alt="node"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Add Notifications
&lt;/h3&gt;

&lt;p&gt;Finally, we can send an Email notification so that we know when the test has finished running.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xlZBWS0A--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1601683674762/yKSxIhbTl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xlZBWS0A--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1601683674762/yKSxIhbTl.png" alt="notification"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Run Pipeline!
&lt;/h3&gt;

&lt;p&gt;If we run our pipeline, our tests will be automated and run every time we pushed to our &lt;code&gt;master&lt;/code&gt; branch.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--N-LOIKCI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1601688173134/v08v_Q1eY.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--N-LOIKCI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1601688173134/v08v_Q1eY.png" alt="pipeline"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Enzyme makes testing components and UI easy for React Apps. With writing tests, automating them can help detect errors early and save development time. &lt;a href="https://buddy.works/?utm_source=hashnode&amp;amp;utm_medium=referral&amp;amp;utm_campaign=content_victoria_lo&amp;amp;utm_content=react_enzyme"&gt;Buddy CI/CD&lt;/a&gt; is a great tool for achieving that automation.&lt;/p&gt;

&lt;p&gt;For more resources on how to create delivery pipelines for React apps with Buddy, please check out the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://youtu.be/mmexLa-o6E8"&gt;Deploying React Apps Webinar by Buddy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://buddy.works/guides/reactjs-zero-downtime-deployment?utm_source=hashnode&amp;amp;utm_medium=referral&amp;amp;utm_campaign=content_victoria_lo&amp;amp;utm_content=react_enzyme"&gt;Build and Deploy React Apps with no Downtime&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

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

</description>
      <category>react</category>
      <category>testing</category>
      <category>tdd</category>
      <category>enzyme</category>
    </item>
  </channel>
</rss>
