<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Make it Easy]]></title><description><![CDATA[Make it Easy]]></description><link>https://jalaj.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Tue, 10 Nov 2026 11:58:44 GMT</lastBuildDate><atom:link href="https://jalaj.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Managing Two GitHub Accounts on Your Mac]]></title><description><![CDATA[If you find yourself juggling work and personal GitHub accounts on your Mac, you're not alone. This guide sets up both accounts once, so the right SSH key…]]></description><link>https://jalaj.hashnode.dev/managing-two-github-accounts-on-your-mac</link><guid isPermaLink="true">https://jalaj.hashnode.dev/managing-two-github-accounts-on-your-mac</guid><category><![CDATA[GitHub]]></category><category><![CDATA[ssh]]></category><category><![CDATA[Git]]></category><dc:creator><![CDATA[Jalaj Gupta]]></dc:creator><pubDate>Wed, 01 Nov 2023 11:19:45 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1698837452077/f6084dea-d2c1-41d4-a4de-a42f831327ef.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>If you find yourself juggling work and personal GitHub accounts on your Mac, you're not alone. This guide sets up both accounts once, so the right SSH key, name, email and commit signature are picked automatically in every repository, with nothing to remember per repo.</p>
<p>I first wrote this guide a while back. Since then I hit two problems it didn't cover: SSH quietly logging in to my personal repos as my <em>work</em> account, and commits made outside my usual folders going out under the wrong identity. This version fixes both and adds commit signing. The commands are for macOS; on Linux, drop the Keychain bits and the rest works the same.</p>
<blockquote>
<p><strong>Quick tip:</strong> for just the commands, scroll to the TL;DR at the bottom.</p>
</blockquote>
<h2>The plan</h2>
<ul>
<li><strong>Work is the default.</strong> Plain <code>git@github.com:…</code> URLs, and the work name and email, apply everywhere unless something says otherwise.</li>
<li><strong>Personal is the exception.</strong> Personal repos use a separate SSH host alias and the personal identity. Git picks them by folder <em>and</em> by remote URL, so a personal repo cloned somewhere unusual still gets it right.</li>
</ul>
<p>Flip the two if most of your work is personal.</p>
<h2>1. Create two SSH keys</h2>
<p>Give them clear names. <code>ed25519</code> keys are shorter and faster than RSA, and GitHub recommends them:</p>
<pre><code class="language-bash">ssh-keygen -t ed25519 -C "jalaj@work.com"     -f ~/.ssh/jalaj-work-github
ssh-keygen -t ed25519 -C "jalaj@personal.com" -f ~/.ssh/jalaj-personal-github
</code></pre>
<p>You'll be asked for a passphrase. Use one; the Keychain step below means you won't have to type it again.</p>
<p>You now have four files in <code>~/.ssh</code>:</p>
<pre><code class="language-plaintext">jalaj-work-github
jalaj-work-github.pub
jalaj-personal-github
jalaj-personal-github.pub
</code></pre>
<p>The <code>.pub</code> files are public; the others are your private keys and never leave your machine.</p>
<h2>2. Add the keys to GitHub</h2>
<p>For <strong>each</strong> account, signed in as that account, go to <strong>Settings → SSH and GPG keys → New SSH key</strong> and add its <code>.pub</code> file <strong>twice</strong>:</p>
<ol>
<li>Key type <strong>Authentication Key</strong>, for push and pull.</li>
<li>Key type <strong>Signing Key</strong>, so your commits show as <em>Verified</em> (step 6).</li>
</ol>
<p>Copy a key to the clipboard with:</p>
<pre><code class="language-bash">pbcopy &lt; ~/.ssh/jalaj-work-github.pub
</code></pre>
<p>Make sure each account is the right one. If your personal key ends up on your work account, everything will <em>seem</em> to work, which is exactly the trap described in the next step.</p>
<h2>3. Configure SSH</h2>
<p>Create <code>~/.ssh/config</code> if it doesn't exist and add:</p>
<pre><code class="language-plaintext"># Work account: the default github.com
Host github.com
  HostName github.com
  User git
  IdentityFile ~/.ssh/jalaj-work-github
  IdentitiesOnly yes
  AddKeysToAgent yes
  UseKeychain yes

# Personal account: an alias for github.com
Host github.com-personal
  HostName github.com
  User git
  IdentityFile ~/.ssh/jalaj-personal-github
  IdentitiesOnly yes
  AddKeysToAgent yes
  UseKeychain yes
</code></pre>
<p><strong>Don't skip</strong> <code>IdentitiesOnly yes</code><strong>.</strong> My original guide left it out, and it cost me. Without it, SSH offers every key loaded in your agent <em>before</em> the <code>IdentityFile</code> you named. If your work key is loaded, the first key offered is the work key, GitHub accepts it, and your "personal" alias quietly logs in as your work account. Pushes to personal repos then fail, or worse, succeed as the wrong person. <code>IdentitiesOnly yes</code> makes each host use only its own key.</p>
<p><code>UseKeychain</code> and <code>AddKeysToAgent</code> keep the passphrase in the macOS Keychain and load the key on first use. To store the passphrases now:</p>
<pre><code class="language-bash">ssh-add --apple-use-keychain ~/.ssh/jalaj-work-github
ssh-add --apple-use-keychain ~/.ssh/jalaj-personal-github
</code></pre>
<p><strong>Test it.</strong> Each host should greet the right account:</p>
<pre><code class="language-bash">ssh -T git@github.com            # Hi jalaj-work!
ssh -T git@github.com-personal   # Hi jalajcodes!
</code></pre>
<p>If both say the same name, re-check <code>IdentitiesOnly</code> and which account each key was added to.</p>
<h2>4. Stop rewriting remote URLs by hand</h2>
<p>The old version of this guide had you run <code>git remote set-url</code> on every personal repo. Git can do that rewrite for you. Add this to <code>~/.gitconfig</code>, with your personal username:</p>
<pre><code class="language-ini">[url "git@github.com-personal:jalajcodes/"]
  insteadOf = git@github.com:jalajcodes/
</code></pre>
<p>Now you can copy the normal clone URL from GitHub (<code>git@github.com:jalajcodes/blog.git</code>), and Git silently connects through <code>github.com-personal</code>. Work URLs are untouched. Check it on any personal repo:</p>
<pre><code class="language-bash">git ls-remote --get-url origin   # git@github.com-personal:jalajcodes/blog.git
</code></pre>
<p>Repos you already set up with <code>git@github.com-personal:…</code> keep working as they are.</p>
<h2>5. Set your name and email automatically</h2>
<p>Create one file per identity.</p>
<p><code>~/.gitconfig-work</code>:</p>
<pre><code class="language-ini">[user]
  name = Jalaj Gupta
  email = jalaj@work.com
</code></pre>
<p><code>~/.gitconfig-personal</code>:</p>
<pre><code class="language-ini">[user]
  name = jalajcodes
  email = jalaj@personal.com
</code></pre>
<p>Then, at the <strong>end</strong> of <code>~/.gitconfig</code>:</p>
<pre><code class="language-ini"># Work identity everywhere by default.
[include]
  path = ~/.gitconfig-work

# Personal identity overrides it. Keep these last: later settings win.
[includeIf "gitdir:~/projects/personal/"]
  path = ~/.gitconfig-personal
[includeIf "hasconfig:remote.*.url:git@github.com:jalajcodes/**"]
  path = ~/.gitconfig-personal
[includeIf "hasconfig:remote.*.url:git@github.com-personal:jalajcodes/**"]
  path = ~/.gitconfig-personal
[includeIf "hasconfig:remote.*.url:https://github.com/jalajcodes/**"]
  path = ~/.gitconfig-personal
</code></pre>
<p>Why it's built this way:</p>
<ul>
<li><strong>A default, not two folder rules.</strong> My original setup only had <code>includeIf</code> rules for <code>~/projects/work/</code> and <code>~/projects/personal/</code>. Anything cloned elsewhere (a temp folder, a tool's scratch directory) matched neither and fell back to whatever global name and email were set. That's how work commits went out under my personal identity. With a default, nothing falls through the cracks.</li>
<li><strong>Order matters.</strong> Git applies settings top to bottom, so the personal includes must come after the work include to override it.</li>
<li><strong>Match by remote, not just folder.</strong> <code>hasconfig:remote.*.url:</code> (Git 2.36 or later; check with <code>git --version</code>) picks the personal identity for any repo whose remote is one of yours, wherever it lives. The three lines cover the plain SSH URL, the alias URL and HTTPS. The folder rule still catches personal-folder repos whose remote isn't yours, such as a clone of someone else's open-source project.</li>
<li><strong>Mind the trailing slash</strong> in <code>gitdir:~/projects/personal/</code>. It makes the rule apply to everything inside the folder.</li>
</ul>
<p><strong>Test it:</strong></p>
<pre><code class="language-bash">cd ~/projects/work/some-repo     &amp;&amp; git config user.email   # jalaj@work.com
cd ~/projects/personal/blog      &amp;&amp; git config user.email   # jalaj@personal.com
</code></pre>
<h2>6. Sign your commits (optional, recommended)</h2>
<p>You already have SSH keys, so you don't need GPG to get the <em>Verified</em> badge. Add to <code>~/.gitconfig-work</code>:</p>
<pre><code class="language-ini">[user]
  signingkey = ~/.ssh/jalaj-work-github.pub
[gpg]
  format = ssh
[gpg "ssh"]
  allowedSignersFile = ~/.ssh/allowed_signers
[commit]
  gpgsign = true
</code></pre>
<p>Add the same block to <code>~/.gitconfig-personal</code> with <code>jalaj-personal-github.pub</code>. Each identity then signs with its own key. Don't leave the signing settings out of the personal file: it would inherit the work key from the default include.</p>
<p><code>allowedSignersFile</code> lets Git verify signatures locally (<code>git log --show-signature</code>). Create it with one line per identity:</p>
<pre><code class="language-bash">echo "jalaj@work.com namespaces=\"git\" $(cut -d' ' -f1,2 ~/.ssh/jalaj-work-github.pub)"         &gt;&gt; ~/.ssh/allowed_signers
echo "jalaj@personal.com namespaces=\"git\" $(cut -d' ' -f1,2 ~/.ssh/jalaj-personal-github.pub)" &gt;&gt; ~/.ssh/allowed_signers
</code></pre>
<p>Watch the section name: it's <code>[gpg "ssh"]</code>, not a nested <code>[ssh]</code> under <code>[gpg]</code>. Git silently ignores the wrong one.</p>
<p>Two things GitHub checks before showing <em>Verified</em>:</p>
<ol>
<li>The key is added to the account as a <strong>Signing Key</strong> (step 2).</li>
<li>The commit's email is a <strong>verified email on that same account</strong> (Settings → Emails).</li>
</ol>
<p><strong>Test it:</strong></p>
<pre><code class="language-bash">git commit --allow-empty -m "signing test"
git log -1 --format='%an &lt;%ae&gt; %G?'   # G means a good signature
</code></pre>
<h2>Troubleshooting</h2>
<table>
<thead>
<tr>
<th>Symptom</th>
<th>Likely cause</th>
</tr>
</thead>
<tbody><tr>
<td><code>ssh -T git@github.com-personal</code> greets your work account</td>
<td><code>IdentitiesOnly yes</code> missing, or the personal key was added to the work account</td>
</tr>
<tr>
<td><code>Permission denied (publickey)</code> on personal repos</td>
<td>The personal key isn't on your personal GitHub account as an <em>Authentication Key</em></td>
</tr>
<tr>
<td>Commits under the wrong name or email</td>
<td>The repo is outside your folders and its remote doesn't match a <code>hasconfig</code> rule; check <code>git config --show-origin user.email</code></td>
</tr>
<tr>
<td>Commit shows <em>Unverified</em> on GitHub</td>
<td>Key not added as a <em>Signing Key</em>, or the commit email isn't verified on that account</td>
</tr>
<tr>
<td><code>gpg.ssh.allowedSignersFile needs to be configured</code></td>
<td><code>allowedSignersFile</code> is in the wrong section or points to a missing file</td>
</tr>
</tbody></table>
<p><code>git config --show-origin --get-regexp 'user\.'</code> shows which file each setting came from, which is the quickest way to debug an include.</p>
<h2>TL;DR</h2>
<ol>
<li><strong>Keys:</strong> <code>ssh-keygen -t ed25519 -C "you@work.com" -f ~/.ssh/jalaj-work-github</code>, and the same for personal.</li>
<li><strong>GitHub:</strong> add each <code>.pub</code> to its own account as both an Authentication Key and a Signing Key.</li>
<li><code>~/.ssh/config</code><strong>:</strong> <code>Host github.com</code> uses the work key, and <code>Host github.com-personal</code> uses the personal key. Give both <code>IdentitiesOnly yes</code>, <code>AddKeysToAgent yes</code> and <code>UseKeychain yes</code>. Test with <code>ssh -T</code>.</li>
<li><code>~/.gitconfig</code><strong>:</strong> <code>[url "git@github.com-personal:you/"] insteadOf = git@github.com:you/</code>, so remotes are rewritten automatically.</li>
<li><code>~/.gitconfig</code><strong>:</strong> <code>[include]</code> the work file first, then <code>includeIf</code> the personal file by folder (<code>gitdir:</code>) and by remote (<code>hasconfig:remote.*.url:</code>), <em>last</em>.</li>
<li><strong>Signing:</strong> each identity file sets <code>signingkey</code>, <code>gpg.format = ssh</code>, <code>allowedSignersFile</code> and <code>commit.gpgsign = true</code>.</li>
</ol>
<p>That's it: one-time setup, and every repo gets the right key, identity and signature without you thinking about it. If you have questions, reach out on Twitter. Thanks for reading!</p>
]]></content:encoded></item><item><title><![CDATA[Keeping the Footer at the Bottom of the Page]]></title><description><![CDATA[In this post I'll show you how to keep the footer where it belongs]]></description><link>https://jalaj.hashnode.dev/keeping-the-footer-at-the-bottom-of-the-page</link><guid isPermaLink="true">https://jalaj.hashnode.dev/keeping-the-footer-at-the-bottom-of-the-page</guid><category><![CDATA[CSS]]></category><category><![CDATA[CSS Grid]]></category><category><![CDATA[flexbox]]></category><category><![CDATA[HTML5]]></category><category><![CDATA[HTML]]></category><dc:creator><![CDATA[Jalaj Gupta]]></dc:creator><pubDate>Thu, 21 Jul 2022 17:20:59 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1622971299349/r9l2SLyPE.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>When creating a website with dyanmic content, we sometimes face a problem where the content on the page doesn't fill the page and the footer, rather than staying in the bottom, moves up leaving blank space beneath it.</p>
<p>There are many ways to solve this problem like,</p>
<ul>
<li><p><strong>Absolute positioning the footer at the bottom</strong> (<em>this  doesn't work when content grows larger than the viewport, the footer gets ‘stuck’ to the bottom of the viewport.</em>)</p>
</li>
<li><p><strong>Using sticky or fixed positioning</strong> (<em>this works if you want your footer to always be visible ;(</em> </p>
</li>
<li><p><strong>Using Flexbox or Grid</strong> (<em>Both of these technologies allows us to get the behaviour we want but in my opinion using grid is easier and more elegant</em>)</p>
</li>
</ul>
<p>I'll explain how to use grid to push footer at the bottom in this post and let's leave flexbox for a later post.</p>
<h2 id="heading-page-structure">Page Structure</h2>
<p>Your page structure should match the html below.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
   <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"page-wrapper"</span>&gt;</span>
       <span class="hljs-tag">&lt;<span class="hljs-name">header</span>&gt;</span>HEADER<span class="hljs-tag">&lt;/<span class="hljs-name">header</span>&gt;</span>
       <span class="hljs-tag">&lt;<span class="hljs-name">main</span>&gt;</span>MAIN<span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span>
       <span class="hljs-tag">&lt;<span class="hljs-name">footer</span>&gt;</span>FOOTER<span class="hljs-tag">&lt;/<span class="hljs-name">footer</span>&gt;</span>
   <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
</code></pre>
<p>Maybe your page structure is somewhat more complex, in that case you need to keep in mind to put your Header, Footer and Main inside a parent container.</p>
<h2 id="heading-applying-css-magic">Applying CSS Magic</h2>
<pre><code class="lang-css"><span class="hljs-selector-class">.page-wrapper</span> {
    <span class="hljs-attribute">min-height</span>: <span class="hljs-number">100vh</span>;
    <span class="hljs-attribute">display</span>: grid;
    <span class="hljs-attribute">grid-template-columns</span>: <span class="hljs-number">100%</span>;
    <span class="hljs-attribute">grid-template-rows</span>: auto <span class="hljs-number">1</span>fr auto;
}
</code></pre>
<p>Let see what each line of above code does.</p>
<ul>
<li><code>min-height: 100vh</code> -  Here we are telling the browser to fill the entire viewport</li>
<li><code>display: grid</code> - This line makes the element a grid container</li>
<li><code>grid-template-columns: 1fr</code> - We are creating a single column and giving it a width of <code>1fr</code>. If that <code>1fr</code> is new to you, it essentially means “take the remaining space” which, in this case, is the entire width of the grid container, <code>.page-wrapper</code>. <br /></li>
</ul>
<p>Now the most important line, </p>
<ul>
<li><p><strong><code>grid-template-rows: auto 1fr auto</code></strong>:  </p>
<ul>
<li>Here we are telling the brower to create three rows. The first and third row, which is the header and footer, respectively, are sized with <code>auto</code>, which means they’ll take up as much space as needed. In other words: no need for hard-coded sizes! (isn't css grid just awesome?)</li>
</ul>
<ul>
<li>The middle row is our content. We’ve assigned it a size of <code>1fr</code> which, again, just means it takes up all of the remaining space that’s left over from the other two rows. If you’re wondering why we aren’t making it <code>auto</code> as well, it’s because the entire grid spans the viewport’s whole height, so we need one section to grow and fill up any unused space. </li>
</ul>
</li>
</ul>
<p>I've prepared a working example on codepen. Go ahead and see what happens when you type more text.</p>
<iframe height="506" style="width:100%" src="https://codepen.io/jalajcodes/embed/Rwpyeqq?height=506&amp;theme-id=dark&amp;default-tab=result">
  See the Pen <a href="https://codepen.io/jalajcodes/pen/Rwpyeqq">Keeping the Footer where it belongs</a> by Jalaj
  (<a href="https://codepen.io/jalajcodes">@jalajcodes</a>) on <a href="https://codepen.io">CodePen</a>.
</iframe>


]]></content:encoded></item><item><title><![CDATA[Creating Page Transitions in React using Framer Motion]]></title><description><![CDATA[Page transitions are animated transition between pages which are used to give websites that extra touch that distinguishes them as a top-notch and worthy…]]></description><link>https://jalaj.hashnode.dev/creating-page-transitions-in-react-using-framer-motion</link><guid isPermaLink="true">https://jalaj.hashnode.dev/creating-page-transitions-in-react-using-framer-motion</guid><category><![CDATA[React]]></category><category><![CDATA[animation]]></category><category><![CDATA[Frontend Development]]></category><dc:creator><![CDATA[Jalaj Gupta]]></dc:creator><pubDate>Thu, 14 Jul 2022 17:21:56 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1620817294069/8mjZL-nk-.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Page transitions are animated transition between pages which are used to give websites that extra touch that distinguishes them as a top-notch and worthy of a good browse, when applied correctly they not only give a sense of liveliness but also helps greatly with navigation.</p>
<p>We all love to see smooth and cool page transitions when browsing websites. I've always been fascinated by them and wanted to learn how to add those to my sites. Libraries like Barba makes it very easy to add them in our site, but they don't work well with React.</p>
<p>Thankfully, Framer Motion makes it possible (and easier) to create sleek and smooth page transitions in React and similar libraries and in this article, I'll show you how. Scroll down to the bottom if you want to see the final demo.</p>
<h2 id="heading-framer-motion">Framer Motion</h2>
<p>The basics of Framer Motion involve creating a <code>motion</code> component and passing some values to set the styles we want an element to start at and animate to. So if I want to have an element to come in from the left when it's rendered, I can play with the <code>x</code> property. Check out the sandbox below, try changing <code>x</code> to <code>y</code> and see what happens.</p>
<pre><code class="lang-jsx">&lt;motion.div
    initial={{ <span class="hljs-attr">opacity</span>: <span class="hljs-number">0</span>, <span class="hljs-attr">x</span>: <span class="hljs-string">"-100vh"</span> }}
    animate={{ <span class="hljs-attr">opacity</span>: <span class="hljs-number">1</span>, <span class="hljs-attr">x</span>: <span class="hljs-number">0</span> }}
&gt;
   <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Animated text<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span></span>
 &lt;/motion.div&gt;
</code></pre>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://codesandbox.io/embed/lingering-sea-67ldq?fontsize=14&amp;theme=dark">https://codesandbox.io/embed/lingering-sea-67ldq?fontsize=14&amp;theme=dark</a></div>
<h2 id="heading-animatepresence">AnimatePresence</h2>
<p>In the above example, you should notice that the top text slides in as it's being mounted, while the normal text renders without any sort of transition. The slide in is smoother, but both texts exhibit the same behavior when they are unmounted. Even though our <code>motion.div</code> component slides in, it doesn't slide out. </p>
<p>To get the desired behavior, we can import and wrap <code>AnimatePresence</code> around our conditionally rendered element. Now we can use the exit prop on our <code>motion.div</code> to have the element slide out as it's being unmounted.</p>
<pre><code class="lang-jsx">&lt;AnimatePresence&gt;
          {isDisplayed &amp;&amp; (
            <span class="xml"><span class="hljs-tag">&lt;&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">motion.div</span>
                <span class="hljs-attr">initial</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">x:</span> "<span class="hljs-attr">-100vh</span>" }}
                <span class="hljs-attr">animate</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">x:</span> <span class="hljs-attr">0</span> }}
                <span class="hljs-attr">exit</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">x:</span> "<span class="hljs-attr">100vh</span>" }}
              &gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Animated text<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
              <span class="hljs-tag">&lt;/<span class="hljs-name">motion.div</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Normal text<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
            <span class="hljs-tag">&lt;/&gt;</span></span>
          )}
&lt;/AnimatePresence&gt;
</code></pre>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://codesandbox.io/embed/small-butterfly-vb9vc?fontsize=14&amp;theme=dark">https://codesandbox.io/embed/small-butterfly-vb9vc?fontsize=14&amp;theme=dark</a></div>
<p>Pretty cool, huh? Let's reuse the above logic to apply animation when a route is changed in this simple react application.</p>
<h3 id="heading-starter-sandbox">Starter Sandbox</h3>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://codesandbox.io/embed/page-transition-starter-q17gq?fontsize=14&amp;theme=dark&amp;view=preview">https://codesandbox.io/embed/page-transition-starter-q17gq?fontsize=14&amp;theme=dark&amp;view=preview</a></div>
<p>Our first step is to wrap our pages inside a <code>&lt;AnimatePresence&gt;</code>. Where we wrap it will depend on where our router is rendering the pages. In our case, we'll do that in <code>App.js</code> file because that's where routes are defined. </p>
<p>AnimatePresence demands that each of its direct children needs to have a unique key prop so it can track their presence in the tree. We can use the <code>location</code> object from the <code>useLocation</code> hook by importing it from <code>react-router-dom</code>.</p>
<p>Here's how the <code>App.js</code> file will look like after making the necessary changes. Notice that I've kept the <code>&lt;Navbar/&gt;</code> component outside of <code>AnimatePresence</code> because we don't want it to animate with the whole page.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { Route, Switch, useLocation } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-router-dom"</span>;
<span class="hljs-keyword">import</span> { AnimatePresence } <span class="hljs-keyword">from</span> <span class="hljs-string">"framer-motion"</span>;

<span class="hljs-keyword">import</span> Home <span class="hljs-keyword">from</span> <span class="hljs-string">"./pages/Home.js"</span>;
<span class="hljs-keyword">import</span> About <span class="hljs-keyword">from</span> <span class="hljs-string">"./pages/About.js"</span>;
<span class="hljs-keyword">import</span> Contact <span class="hljs-keyword">from</span> <span class="hljs-string">"./pages/Contact.js"</span>;
<span class="hljs-keyword">import</span> Navbar <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/Navbar.js"</span>;

<span class="hljs-keyword">const</span> App = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> location = useLocation();

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Navbar</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">AnimatePresence</span> <span class="hljs-attr">exitBeforeEnter</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Switch</span> <span class="hljs-attr">location</span>=<span class="hljs-string">{location}</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{location.pathname}</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">Route</span> <span class="hljs-attr">exact</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"/"</span> <span class="hljs-attr">component</span>=<span class="hljs-string">{Home}</span> /&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"/about"</span> <span class="hljs-attr">component</span>=<span class="hljs-string">{About}</span> /&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"/contact"</span> <span class="hljs-attr">component</span>=<span class="hljs-string">{Contact}</span> /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">Switch</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">AnimatePresence</span>&gt;</span>
    <span class="hljs-tag">&lt;/&gt;</span></span>
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<p>If you look closely you will notice that I've added a prop called <code>exitBeforeEnter</code> to AnimatePresence. This prop guarantees that our component will have unmounted (i.e. completed its exit animation) before allowing the new component to animate in. If we remove this prop the exit and enter animation will take place at the same time and we won't be able to see the enter animation while changing routes.</p>
<p>Now all that's left is to use a <code>motion</code> component in our pages and define our <code>initial</code>, <code>animate</code>, and <code>exit</code> props like we did earlier!</p>
<p>So, we'll wrap the return value of all the three pages of our site with a <code>motion.div</code> component and add the necessary props. We'll also add a <code>transition</code> prop to change the duration of the animation so that we can see it more easily. Here's how <code>Home.js</code> will look like after making the changes.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> Banner <span class="hljs-keyword">from</span> <span class="hljs-string">"../components/Banner.js"</span>;
<span class="hljs-keyword">import</span> { motion } <span class="hljs-keyword">from</span> <span class="hljs-string">"framer-motion"</span>;

<span class="hljs-keyword">const</span> Home = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">motion.div</span>
      <span class="hljs-attr">initial</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">opacity:</span> <span class="hljs-attr">0</span>, <span class="hljs-attr">x:</span> <span class="hljs-attr">-200</span> }}
      <span class="hljs-attr">animate</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">opacity:</span> <span class="hljs-attr">1</span>, <span class="hljs-attr">x:</span> <span class="hljs-attr">0</span> }}
      <span class="hljs-attr">exit</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">opacity:</span> <span class="hljs-attr">0</span>, <span class="hljs-attr">x:</span> <span class="hljs-attr">200</span> }}
      <span class="hljs-attr">transition</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">duration:</span> <span class="hljs-attr">0.7</span> }}
    &gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Banner</span> <span class="hljs-attr">title</span>=<span class="hljs-string">"Welcome stranger!"</span> <span class="hljs-attr">subtitle</span>=<span class="hljs-string">"Put something witty here!"</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"container"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Welcome<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>
          Lorem ipsum........
        <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>
          Maecenas dapibus.......
        <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">motion.div</span>&gt;</span></span>
  );
};
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Home;
</code></pre>
<h3 id="heading-final-demo">Final Demo</h3>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://codesandbox.io/embed/page-transition-finished-owjid?fontsize=14&amp;theme=dark">https://codesandbox.io/embed/page-transition-finished-owjid?fontsize=14&amp;theme=dark</a></div>
<h2 id="heading-conclusion">Conclusion</h2>
<p>I hope you enjoyed reading the article and learned something from it.</p>
<p>We have only scratched the surface of what framer motion can do. In the next post, we'll use some advanced features of framer motion like <code>AnimateSharedLayout</code> to create a fairly complex page transition.</p>
]]></content:encoded></item><item><title><![CDATA[Reversing a String in Javascript]]></title><description><![CDATA[Reversing a string is a very common interview question which can be asked to you in phone interview or screening.]]></description><link>https://jalaj.hashnode.dev/reversing-a-string-in-javascript</link><guid isPermaLink="true">https://jalaj.hashnode.dev/reversing-a-string-in-javascript</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[algorithms]]></category><category><![CDATA[interview]]></category><dc:creator><![CDATA[Jalaj Gupta]]></dc:creator><pubDate>Thu, 16 Jun 2022 17:25:13 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1621764485915/RqlzDtWVj.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In this post I will explain different methods to reverse the string using javascript, so without any further ado let get right into it.</p>
<p>Below given code is how I wrote the solution at first, I'll explain it and after that discuss other methods of solving the problem.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">reverseString</span>(<span class="hljs-params">str</span>) </span>{
    <span class="hljs-keyword">let</span> arr = str.split(<span class="hljs-string">""</span>);
    arr.reverse();
    str = arr.join(<span class="hljs-string">""</span>);
    <span class="hljs-keyword">return</span> str;
}
</code></pre>
<p>What above code does is it first uses the <code>split</code> function which splits the passed string object into array of strings by separating strings into substrings.</p>
<p>After that I reversed the array we created above using <code>reverse</code> method. In the last I joined all the elements of array into string using the <code>join</code> method and returned the string.</p>
<p>So that's my solution to the challenge. Now let’s talk about other ways.</p>
<p>To make above code shorter we can chain methods and return immediately.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">reverseString</span>(<span class="hljs-params">str</span>)</span>{
    <span class="hljs-keyword">return</span> str.split(<span class="hljs-string">""</span>).reverse().join(<span class="hljs-string">""</span>);
}
</code></pre>
<p>While we're at it we can make it even shorter if we use ES6 arrow function syntax.</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> reverseString = <span class="hljs-function"><span class="hljs-params">str</span> =&gt;</span> str.split(<span class="hljs-string">""</span>).reverse().join(<span class="hljs-string">""</span>);
</code></pre>
<p>Now imagine your interviewer saying, 'Great, now solve it without using these inbuilt methods with a simple for loop.'</p>
<p>Don't sweat yet! Below I'll tell how to do just that...</p>
<h2 id="heading-reversing-a-string-using-for-loop">Reversing a string using for loop</h2>
<p>We can also reverse a String without using builtin methods like reverse by using for loop.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">reverseString</span>(<span class="hljs-params">str</span>) </span>{
    <span class="hljs-keyword">let</span> newString = <span class="hljs-string">""</span>;
    <span class="hljs-keyword">for</span> (i = str.length - <span class="hljs-number">1</span>; i &gt;= <span class="hljs-number">0</span>; i--) {
        newString += str[i];
    }
    <span class="hljs-keyword">return</span> newString;
}
</code></pre>
<p>Okay so what I am doing in this code is at first I declared a new variable to hold our reversed string.</p>
<p>After that I created a for loop. The starting point of the for loop is <code>str.length - 1</code>. which is the index of the last character of the string. As long as the value of <code>i</code> is greater than or equal to zero the loop will go on and we decrement the value of i with each iteration. </p>
<p>Checkout the pseudocode explaining the process below.</p>
<p>For example let’s suppose the string passed to the function is “hello”.</p>
<pre><code>Now hello’s length equals <span class="hljs-number">5</span>
For each iteration: i <span class="hljs-operator">=</span> str.<span class="hljs-built_in">length</span> – <span class="hljs-number">1</span> and newString <span class="hljs-operator">=</span> newString <span class="hljs-operator">+</span> str[i]
First iteration: i <span class="hljs-operator">=</span> <span class="hljs-number">5</span> – <span class="hljs-number">1</span> <span class="hljs-operator">=</span> <span class="hljs-number">4</span>, newString <span class="hljs-operator">=</span> “” <span class="hljs-operator">+</span> “o” <span class="hljs-operator">=</span> “o”
Second iteration: i <span class="hljs-operator">=</span> <span class="hljs-number">4</span> – <span class="hljs-number">1</span> <span class="hljs-operator">=</span> <span class="hljs-number">3</span>, newString <span class="hljs-operator">=</span> “o” <span class="hljs-operator">+</span> “l” <span class="hljs-operator">=</span> “ol”
Third iteration: i <span class="hljs-operator">=</span> <span class="hljs-number">3</span> – <span class="hljs-number">1</span> <span class="hljs-operator">=</span> <span class="hljs-number">2</span>, newString <span class="hljs-operator">=</span> “ol” <span class="hljs-operator">+</span> “l” <span class="hljs-operator">=</span> “oll”
Fourth iteration: i <span class="hljs-operator">=</span> <span class="hljs-number">2</span> – <span class="hljs-number">1</span> <span class="hljs-operator">=</span> <span class="hljs-number">1</span>, newString <span class="hljs-operator">=</span> “oll” <span class="hljs-operator">+</span> “e” <span class="hljs-operator">=</span> “olle”
Fifth iteration: i <span class="hljs-operator">=</span> <span class="hljs-number">1</span> – <span class="hljs-number">1</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>, newString <span class="hljs-operator">=</span> “olle” <span class="hljs-operator">+</span> “h” <span class="hljs-operator">=</span> “olleh”
End of the FOR Loop
</code></pre><p>Aaaand in the last we returned the reversed string.</p>
<p>Hope you got that! If not then don’t hesitate to comment and I’ll be more than happy to help.</p>
<p>Still awake? Let's see yet another method to reverse your strings.</p>
<h2 id="heading-reversing-string-using-recursion">Reversing string using Recursion</h2>
<p>In this method, I will use two methods: the <code>String.prototype.substr()</code> method and the <code>String.prototype.charAt()</code> method.</p>
<p>The <code>substr()</code> method returns the characters in a string beginning at the specified location through the specified number of characters.</p>
<pre><code class="lang-js"><span class="hljs-string">"hello"</span>.substr(<span class="hljs-number">1</span>); <span class="hljs-comment">// "ello"</span>
</code></pre>
<p>The <code>charAt()</code> method returns the specified character from a string.</p>
<pre><code class="lang-js"><span class="hljs-string">"hello"</span>.charAt(<span class="hljs-number">0</span>); <span class="hljs-comment">// "h"</span>
</code></pre>
<p>Now the whole code,</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">reverseString</span>(<span class="hljs-params">str</span>) </span>{
    <span class="hljs-keyword">if</span> (str === <span class="hljs-string">""</span>) { <span class="hljs-comment">// This is the terminal case that will end the recursion</span>
        <span class="hljs-keyword">return</span> <span class="hljs-string">""</span>;
    } <span class="hljs-keyword">else</span> {
        <span class="hljs-keyword">return</span> reverseString(str.substr(<span class="hljs-number">1</span>)) + str.charAt(<span class="hljs-number">0</span>);
    }
}
reverseString(<span class="hljs-string">"hello"</span>); <span class="hljs-comment">// olleh</span>
</code></pre>
<p>Explanation: </p>
<pre><code class="lang-js">First Part <span class="hljs-keyword">of</span> the recursion method

  We need to remember that there won’t be just one call, we’ll have several nested calls

  Each call will <span class="hljs-keyword">return</span>       reverseString(str.subst(<span class="hljs-number">1</span>))   +    str.charAt(<span class="hljs-number">0</span>)

  <span class="hljs-number">1</span>st call – reverseString(<span class="hljs-string">"Hello"</span>)   will <span class="hljs-keyword">return</span>   reverseString(<span class="hljs-string">"ello"</span>)    + <span class="hljs-string">"h"</span>
  <span class="hljs-number">2n</span>d call – reverseString(<span class="hljs-string">"ello"</span>)    will <span class="hljs-keyword">return</span>   reverseString(<span class="hljs-string">"llo"</span>)      + <span class="hljs-string">"e"</span>
  <span class="hljs-number">3</span>rd call – reverseString(<span class="hljs-string">"llo"</span>)     will <span class="hljs-keyword">return</span>   reverseString(<span class="hljs-string">"lo"</span>)       + <span class="hljs-string">"l"</span>
  <span class="hljs-number">4</span>th call – reverseString(<span class="hljs-string">"lo"</span>)      will <span class="hljs-keyword">return</span>   reverseString(<span class="hljs-string">"o"</span>)        + <span class="hljs-string">"l"</span>
  <span class="hljs-number">5</span>th call – reverseString(<span class="hljs-string">"o"</span>)       will <span class="hljs-keyword">return</span>   reverseString(<span class="hljs-string">""</span>)          + <span class="hljs-string">"o"</span>

Second part <span class="hljs-keyword">of</span> the recursion method

  The method hits the <span class="hljs-keyword">if</span> condition and the most highly nested call returns immediately

  <span class="hljs-number">5</span>th call will <span class="hljs-keyword">return</span> reverseString(<span class="hljs-string">""</span>) + <span class="hljs-string">"o"</span> = <span class="hljs-string">"o"</span>
  <span class="hljs-number">4</span>th call will <span class="hljs-keyword">return</span> reverseString(<span class="hljs-string">"o"</span>) + <span class="hljs-string">"l"</span> = <span class="hljs-string">"o"</span> + <span class="hljs-string">"l"</span>
  <span class="hljs-number">3</span>rd call will <span class="hljs-keyword">return</span> reverseString(<span class="hljs-string">"lo"</span>) + <span class="hljs-string">"l"</span> = <span class="hljs-string">"o"</span> + <span class="hljs-string">"l"</span> + <span class="hljs-string">"l"</span>
  <span class="hljs-number">2n</span>d call will <span class="hljs-keyword">return</span> reverserString(<span class="hljs-string">"llo"</span>) + <span class="hljs-string">"e"</span> = <span class="hljs-string">"o"</span> + <span class="hljs-string">"l"</span> + <span class="hljs-string">"l"</span> + <span class="hljs-string">"e"</span>
  <span class="hljs-number">1</span>st call will <span class="hljs-keyword">return</span> reverserString(<span class="hljs-string">"ello"</span>) + <span class="hljs-string">"h"</span> = <span class="hljs-string">"o"</span> + <span class="hljs-string">"l"</span> + <span class="hljs-string">"l"</span> + <span class="hljs-string">"e"</span> + <span class="hljs-string">"h"</span>
</code></pre>
<p>In the end we get the reversed string.</p>
<p>So this one was heavy and I doubt if someone could come up with this solution on his own in interview. If you don't understand this solution right now its totally fine.</p>
<p>Now continuing on our tradition from above, here's the shortened version of above code using arrow functions and ternary operators.</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> reversedString = <span class="hljs-function"><span class="hljs-params">str</span> =&gt;</span> (str === <span class="hljs-string">""</span>) ? <span class="hljs-string">""</span> : reverseString(str.substr(<span class="hljs-number">1</span>)) + str.charAt(<span class="hljs-number">0</span>);
</code></pre>
<h3 id="heading-parting-words">Parting words</h3>
<p>Hope you learned something from the article. Reversing a string is a simple problem which can be useful in solving more complex problems in interview or maybe if you are lucky enough you'll be asked just to reverse a string ;)</p>
<p>See you in next post, till then Happy Coding!</p>
]]></content:encoded></item><item><title><![CDATA[The easy-peasy React state management library]]></title><description><![CDATA[Every application needs to manage state. In React, we can go a long way using hooks, and in particular useState(), and passing props around. In my personal…]]></description><link>https://jalaj.hashnode.dev/the-easy-peasy-react-state-management-library</link><guid isPermaLink="true">https://jalaj.hashnode.dev/the-easy-peasy-react-state-management-library</guid><category><![CDATA[React]]></category><category><![CDATA[Redux]]></category><dc:creator><![CDATA[Jalaj Gupta]]></dc:creator><pubDate>Sun, 23 May 2021 13:00:50 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1621774817073/jV7BSYKiE.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Every application needs to manage state. In React, we can go a long way using hooks, and in particular <code>useState()</code>, and passing props around.</p>
<p>In my personal projects when things get more complicated than that, I like to immediately jump to a state management library. One of my favorites lately is <code>easy-peasy</code>.</p>
<p>It builds on top of Redux, and it provides a simpler way to interact with state.</p>
<p>I like to keep my code as simple as possible. Simple is understandable. Simple is beautiful.</p>
<p>Complexity should be avoided at all costs, and if possible hidden away in libraries that expose a simple interface to us. It’s the case of this library, and that’s why I like it!</p>
<p>Install it using:</p>
<pre><code class="lang-sh">yarn add easy-peasy
</code></pre>
<p>First of all we need to create a <strong>store</strong>. The store is the place where we’ll store our state, and the functions needed to modify it.</p>
<p>Create the store in the file <code>store.js</code> in the root of your project, with this content:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { createStore, action } <span class="hljs-keyword">from</span> <span class="hljs-string">'easy-peasy'</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> createStore({})
</code></pre>
<p>We’ll add more things to this file later.</p>
<p>Now let's wrap our React app into the <code>StoreProvider</code> component provided by <code>easy-peasy</code>. It depends on what you use. With <code>create-react-app</code> for example, I'll add this to my <code>index.js</code> file:</p>
<pre><code class="lang-js"><span class="hljs-comment">//...</span>
<span class="hljs-keyword">import</span> { StoreProvider } <span class="hljs-keyword">from</span> <span class="hljs-string">'easy-peasy'</span>
<span class="hljs-keyword">import</span> store <span class="hljs-keyword">from</span> <span class="hljs-string">'../store'</span>

ReactDOM.render(
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">React.StrictMode</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">StoreProvider</span> <span class="hljs-attr">store</span>=<span class="hljs-string">{store}</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">App</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">StoreProvider</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">React.StrictMode</span>&gt;</span></span>,
  <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'root'</span>)
)
</code></pre>
<p>This operation makes now our store available in every component of the app.</p>
<p>Now we're ready to go in the <code>store.js</code> file and add some state, and some actions to change that state.</p>
<p>Let’s do a simple example. We can create a <code>name</code> state, and we create a <code>setName</code> action to change the name:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { createStore, action } <span class="hljs-keyword">from</span> <span class="hljs-string">'easy-peasy'</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> createStore({
  <span class="hljs-attr">name</span>: <span class="hljs-string">''</span>,
  <span class="hljs-attr">setName</span>: action(<span class="hljs-function">(<span class="hljs-params">state, payload</span>) =&gt;</span> {
    name = payload
  })
})
</code></pre>
<p>Now inside any component of our app we can import <code>useStoreState</code> and <code>useStoreActions</code> from <code>easy-peasy</code>:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { useStoreState, useStoreActions } <span class="hljs-keyword">from</span> <span class="hljs-string">'easy-peasy'</span>
</code></pre>
<p>We use <code>useStoreState</code> to access the store state properties:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> name = useStoreState(<span class="hljs-function">(<span class="hljs-params">state</span>) =&gt;</span> state.name)
</code></pre>
<p>and <code>useStoreActions</code> to access the actions we defined:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> setName = useStoreActions(<span class="hljs-function">(<span class="hljs-params">actions</span>) =&gt;</span> actions.setName)
</code></pre>
<p>Now we can call this action whenever something happens in our app, for example if we click a button:</p>
<pre><code class="lang-jsx">&lt;button
  onClick={<span class="hljs-function">() =&gt;</span> {
    setName(<span class="hljs-string">'newname'</span>)
  }}
&gt;
  <span class="hljs-built_in">Set</span> name
&lt;/button&gt;
</code></pre>
<p>Now any other component that is accessing the state through <code>useStoreState()</code> will see the value updated.</p>
<p>This is a simple example but it all starts from this. We can add as many state variables and as many actions we want, and I found that centralizing it all to a <code>store.js</code> file makes the application very easy to scale.</p>
]]></content:encoded></item><item><title><![CDATA[GiTrack - Statistics and Issue Tracker for Open Source Enthusiasts]]></title><description><![CDATA[My submission for the #AmplifyHashnode GiTrack website to track assigned Issues, view statistics, and more...]]></description><link>https://jalaj.hashnode.dev/gitrack-statistics-and-issue-tracker-for-open-source-enthusiasts</link><guid isPermaLink="true">https://jalaj.hashnode.dev/gitrack-statistics-and-issue-tracker-for-open-source-enthusiasts</guid><category><![CDATA[Amplify Hashnode]]></category><category><![CDATA[hackathon]]></category><category><![CDATA[Next.js]]></category><category><![CDATA[Firebase]]></category><dc:creator><![CDATA[Jalaj Gupta]]></dc:creator><pubDate>Sat, 27 Feb 2021 14:02:50 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1614435522263/VtKRIBVv-.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="introduction">👋 <strong>Introduction</strong></h1>
<p>Hi folks!</p>
<p>First of all, I want to thank the Hashnode team for providing us with the opportunity to get out of our comfort zone (or should I say Tutorial Hell 😉) and actually build and deploy something into the real world. This is my first ever hackathon and I am very excited to share what I created with all of you.</p>
<p>I am Jalaj, a student from India currently pursuing my bachelor's degree. I started contributing to Open source last year in December since then I've contributed to 10+ repositories with over 30+ merged pull requests. </p>
<p>Working in Open Source has taught me a lot of things and I would recommend everyone to try it, we get to learn to work with other people, learn and improve by reading code and a lot of other things which one simply can't get by just watching a tutorial on Youtube or Udemy and creating simple and trivial apps. </p>
<h2 id="introducing-gitrack">✨ <strong>Introducing</strong> <strong>G</strong>i<strong>T</strong>rack</h2>
<ul>
<li><a target="_blank" href="https://dev.d1bycrk5tmifaz.amplifyapp.com/"><strong>Demo</strong></a></li>
<li><a target="_blank" href="https://github.com/jalajcodes/gitrack">Github</a></li>
</ul>
<p>I like to keep track of things and while contributing simultaneously to various repositories, it sometimes gets difficult to keep track of assigned issues and pull requests.  To solve that problem, I built GiTrack, a website for Open Source Enthusiasts to keep track of issues assigned to them and arrange them in a nice looking Kanban board.</p>
<p>I wanted to create something useful but also simple enough for everyone to use, that's why I went with a Kanban board (coz everyone's familiar with it? 😅) besides that I also decide to add a profile page to the website that shows some useful statistics to the user such as the Total number of their contributions, issues they have opened, pull requests they have created, their current streak and a lot more...</p>
<p>Github has their own project board for a per-project basis, but the idea behind GiTrack is to be an issue tracker for all repositories, owned or otherwise, plus you get some nice statistics. </p>
<p>Here's what I had in mind when I started building the website:</p>
<ul>
<li>It should allow users to log in via Github. (who wants to type their email and password?)</li>
<li>It should have a beautiful homepage that lists features of the website.</li>
<li>It should allow users to fetch currently assigned issues directly into the board.</li>
<li>Board state should be saved every time a user makes any changes. So that the next time they visit they can start where they left.</li>
<li>Profile page should list different stats that might be useful for users.</li>
</ul>
<p>I used AWS Amplify to host the site. Amplify's CLI is a dream come true for developers. Just one command and the site is deployed live. </p>
<h2 id="screenshots">💻 Screenshots</h2>
<h3 id="light-mode">Light mode:</h3>
<p><strong>Home page</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1614430330590/eq9F3UEsB.png" alt="Homepage" /></p>
<p><strong>Login Page</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1614430835626/4ycBGDA0Z.png" alt="login screen" /></p>
<p><strong>Statistics</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1614749690295/sf306weEGG.png" alt="ScreenHunter Mar. 03 10.44.png" />
<strong>Issue Tracker</strong>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1614430776186/1gyVU5j8k.png" alt="issue tracker" /></p>
<h3 id="dark-mode">Dark mode:</h3>
<p><strong>Home page</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1614749236268/BG9e34Dpi.png" alt="ScreenHunter Mar. 03 10.37.png" /></p>
<p><strong>Login page</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1614749283639/O5ZQRXxDH.png" alt="ScreenHunter Mar. 03 10.40.png" />
<strong>Statistics</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1614749360358/K4bAOW2bBB.png" alt="ScreenHunter Mar. 03 10.43.png" />
<strong>Issue tracker</strong>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1614749394527/ggt8GF7Qv.png" alt="ScreenHunter Mar. 03 10.48.png" /></p>
<h2 id="features">👀 Features</h2>
<ul>
<li>Issue Tracker</li>
<li>Github Statistics</li>
<li>Fetch issues assigned to you directly from Github.</li>
<li>Client Side Storage using IndexedDB for super fast page loads.</li>
<li>Persistent board state, so no worries about accidentally closing the page.</li>
<li>Beautiful UI</li>
<li>Open Source (checkout code on <a target="_blank" href="https://github.com/jalajcodes">github</a>)</li>
<li>One Click Login using Github OAuth</li>
</ul>
<p>Here's a quick video showing how to use the issue tracker.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://youtu.be/2DoJbP9700Y">https://youtu.be/2DoJbP9700Y</a></div>
<h2 id="build-process">🏢 Build Process</h2>
<p>I really wanted to get out of my comfort zone and build something with the technologies I've never used before. In the tech stack listed below, I was only familiar with Typescript before I started this project.</p>
<h4 id="tech-stack">👩‍💻 Tech Stack</h4>
<ul>
<li>Next JS</li>
<li>Typescript</li>
<li>AWS Amplify</li>
<li>Firebase</li>
<li>Chakra UI</li>
</ul>
<p>I read about this hackathon at the starting of February and I created a repo and pushed the initial commit the on 14th. But after that, I started procrastinating didn't do anything for the next few days. It was 22 when I got some confidence boost and started working on the project again.</p>
<p>I used <a target="_blank" href="https://openchakra.app/">OpenChakra</a> to quickly create a basic boilerplate for the pages and edited it to fit my needs. I chose firebase to do the authentication and storage. I really wanted to use Amplify for auth but they don't have Github OAuth. Anyways, after getting done with a basic MVP of the project I deployed it on Amplify using its CLI. </p>
<p>Here are some of the packages I used: </p>
<ul>
<li><code>nextjs</code> : for building the app</li>
<li><code>typescript</code> for writing <strong>type safe</strong> code</li>
<li><code>react-trello</code> for issue tracker</li>
<li><code>chakra-ui</code> as a UI framework</li>
<li><code>firebase</code> for auth and storage</li>
<li><code>localforage</code> for storing data in indexedDB</li>
<li><code>octokit</code> as a github rest client</li>
</ul>
<h2 id="challenges-faced-while-developing">🏆 Challenges faced while developing</h2>
<ul>
<li><p>Persist board state: I wanted to store board state every time a user makes any change to the tracker board. I could use firebase for this but I wanted to keep it fast. So my only option was to store data in the browser. As the data was complex I was having trouble storing it in localstorage, I did some research and decided to use IndexedDB.</p>
</li>
<li><p>As I wasn't requesting any special permissions while using GitHub OAuth I had to find an alternative way to get assigned issues for the user.  I did some research and used github issues and pull requests search endpoint to get the required data.</p>
</li>
<li><p>Persist login state: Firebase provides a function just for this purpose but it wasn't working as I expected it to be, it logged the user in without checking if there is data in IndexedDB or not so I had to modify it with added logic of fetching the state from indexedDB first and if there is no data in DB then don't log the user in.</p>
</li>
</ul>
<h2 id="whats-next">🌟 What's Next</h2>
<ul>
<li>Add Dark Mode<ul>
<li>Update <strong>3/3/21</strong>: Dark mode added. (Check Screenshots above)</li>
</ul>
</li>
<li>Allow users to get issues they recently commented on.</li>
<li>Allow users to get their merged pull requests. </li>
<li>Keyboard shortcuts</li>
</ul>
<h2 id="conclusion">🥳 Conclusion</h2>
<p>All in all, it was fun working on the project in a strict timeline. Once again thanks to the folks over at Hashnode for this opportunity. </p>
<p>Thanks to the reader for reading this far, I hope you like the app, and please don't hesitate to message me or comment below if you have any feedback.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://github.com/jalajcodes/gitrack">https://github.com/jalajcodes/gitrack</a></div>
]]></content:encoded></item></channel></rss>