<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Dinesh Pandiyan</title>
    <description>The latest articles on Forem by Dinesh Pandiyan (@flexdinesh).</description>
    <link>https://forem.com/flexdinesh</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%2F41138%2F48e86113-f232-44e1-980d-1193fd2a50b8.jpg</url>
      <title>Forem: Dinesh Pandiyan</title>
      <link>https://forem.com/flexdinesh</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/flexdinesh"/>
    <language>en</language>
    <item>
      <title>Build Static Blogs with Keystone</title>
      <dc:creator>Dinesh Pandiyan</dc:creator>
      <pubDate>Thu, 08 Sep 2022 23:04:53 +0000</pubDate>
      <link>https://forem.com/flexdinesh/build-static-blogs-with-keystone-28kn</link>
      <guid>https://forem.com/flexdinesh/build-static-blogs-with-keystone-28kn</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TLDR:&lt;/strong&gt; A local &lt;strong&gt;Keystone&lt;/strong&gt; server to author content combined with &lt;strong&gt;Next.js&lt;/strong&gt; static HTML export gives you a powerful tool to generate static blogs.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://github.com/flexdinesh/keystone-static-blog" rel="noopener noreferrer"&gt;Keystone Static Blog&lt;/a&gt; is an open source template to build static blogs with &lt;a href="https://keystonejs.com/" rel="noopener noreferrer"&gt;Keystone&lt;/a&gt; and &lt;a href="https://nextjs.org/docs/advanced-features/static-html-export" rel="noopener noreferrer"&gt;Next.js&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The template comes bundled with lots of goodies —&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Beautiful&lt;/strong&gt;, &lt;strong&gt;performant&lt;/strong&gt; and &lt;strong&gt;fully responsive&lt;/strong&gt; blog template.&lt;/li&gt;
&lt;li&gt;Built with &lt;a href="https://tailwindcss.com/" rel="noopener noreferrer"&gt;Tailwind&lt;/a&gt; and it comes with a nice &lt;strong&gt;dark/light mode&lt;/strong&gt; that's easily customisable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SEO optimised&lt;/strong&gt; and &lt;strong&gt;accessible&lt;/strong&gt; from the start. Lighthouse score is 100 for both SEO and Accessibility.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/flexdinesh/keystone-static-blog" rel="noopener noreferrer"&gt;keystone-static-blog&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Preview:&lt;/strong&gt; &lt;a href="https://staticblogswithkeystone.netlify.app" rel="noopener noreferrer"&gt;staticblogswithkeystone.netlify.app&lt;br&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Here's how it works
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Content is authored in your local environment using Keystone's powerful rich text editor.&lt;/li&gt;
&lt;li&gt;Content is fetched from Keystone's auto-generated GraphQL API during build-time.&lt;/li&gt;
&lt;li&gt;The blog is prerendered as a static website using Next.js's static HTML export.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Content authoring with Keystone
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://keystonejs.com/" rel="noopener noreferrer"&gt;Keystone&lt;/a&gt; is a powerful backend framework to build content driven websites. Keystone operates on &lt;strong&gt;schema driven development&lt;/strong&gt; mental model where you describe the schema you want in JavaScript and Keystone automatically generates a fully functional GraphQL API for you in seconds. And to top it off, Keystone comes built-in with a feature-rich Admin UI to manage your content visually.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx36svipkigda4cytwo45.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx36svipkigda4cytwo45.png" alt="Keystone's Admin UI"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Keystone's document field is powered by an incredible rich text editor. All the default components (headings, paragraphs, bold, italics, code blocks, etc) that you usually use to write a blog post are supported by the editor out of the box. The editor exposes an intuitive API to build your own custom components and that's what makes this blog template stand out. This template includes a few custom components in the editor that would be useful in a blog post. &lt;em&gt;Eg. Hero image, Tweet embed, YouTube embed, callout, custom blockquote and custom code blocks with syntax highlighting using&lt;/em&gt; &lt;a href="https://prismjs.com/" rel="noopener noreferrer"&gt;prism&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh3xzcwsxkb3iwcedj8vy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh3xzcwsxkb3iwcedj8vy.png" alt="Content authoring using Keystone's Rich Text Editor"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpwxpzwqiyokmh3dov780.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpwxpzwqiyokmh3dov780.png" alt="Custom components in Keystone's Rich Text Editor"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Keystone gives us a GraphQL API right from the get go to work with our data, meaning, applications built with Keystone are &lt;a href="https://jamstack.org/" rel="noopener noreferrer"&gt;Jamstack&lt;/a&gt; ready right from the start. &lt;/p&gt;

&lt;p&gt;On top of that, Keystone supports &lt;a href="https://www.sqlite.org/index.html" rel="noopener noreferrer"&gt;SQLite&lt;/a&gt; database. SQLite is a super compact and convenient file system based database. This means that in an SQLite based Keystone server, you don't have to host your server anywhere and instead use it only in your local environments, &lt;strong&gt;all your data can live alongside your code as a simple, small file and changes to your content can be committed to your repo just like any other file&lt;/strong&gt;. 🤯 This is what makes this whole template a super powerful alternative to MDX based blogs where the workflow is exactly the same — make changes in your local and commit your content to your repo.&lt;/p&gt;

&lt;h3&gt;
  
  
  Next.js static export
&lt;/h3&gt;

&lt;p&gt;The template is just a simple prerendered static blog built with &lt;a href="https://nextjs.org/docs/advanced-features/static-html-export" rel="noopener noreferrer"&gt;Next.js static HTML export&lt;/a&gt;. When you run the build, content data is fetched from the local keystone GraphQL API and &lt;strong&gt;the entire blog is exported as a bunch of HTML, CSS and JavaScript files&lt;/strong&gt; that can be statically hosted anywhere for free. &lt;/p&gt;

&lt;h2&gt;
  
  
  Tailwind makes wire-framing redundant
&lt;/h2&gt;

&lt;p&gt;The blog template is built using &lt;a href="https://tailwindcss.com/" rel="noopener noreferrer"&gt;Tailwind&lt;/a&gt; and comes with &lt;strong&gt;dark/light mode&lt;/strong&gt;. The greatest thing about Tailwind is not the tool itself but its opinionated design tokens and the preset based approach (&lt;em&gt;popularly known as utility based approach&lt;/em&gt;) to building and styling interfaces. Even if you don't know how your interfaces are exactly going to look, you can play around with the presets and somehow end up with a beautiful looking website. That's how good the default Tailwind design tokens are. &lt;em&gt;&lt;strong&gt;Every time I work with Tailwind I feel like a magician pulling user interfaces out of thin air.&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foxpc4fvdpqnt73k6zczd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foxpc4fvdpqnt73k6zczd.png" alt="Dark mode and light mode"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Comparison with MDX based static blogs
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://nextjs.org/docs/advanced-features/using-mdx" rel="noopener noreferrer"&gt;Authoring content using MDX&lt;/a&gt; is great. I love it. I use MDX to author content in a lot of projects. You write something in a file and commit to your repo, it's as simple as that. But simplicity also means that it's limited. Customisation is a pain. If you want to build your own custom components (&lt;em&gt;Embedded videos, tweets, a deeply nested component, etc&lt;/em&gt;) to use in your content you gotta jump through hoops and hope someone has made a plugin that suits your requirements. And forget about visually looking at your content while authoring. And that's why I prefer to author my content with a visual editor like Keystone's document editor that also supports MDX syntax to author content.&lt;/p&gt;

&lt;p&gt;At the end of the day, MDX and local Keystone based static blogs are more of less the same in terms of how you ship content —&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You author content in your local environment.&lt;/li&gt;
&lt;li&gt;You prerender your blog.&lt;/li&gt;
&lt;li&gt;You commit your content along with your code to your repo.&lt;/li&gt;
&lt;li&gt;You host your prerendered website as static files.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  That's all folks
&lt;/h2&gt;

&lt;p&gt;I built this &lt;a href="https://staticblogswithkeystone.netlify.app/" rel="noopener noreferrer"&gt;template&lt;/a&gt; as an experiment to see if Keystone could be a good alternative for MDX and had a ton of fun building it. I'm gonna add a handful of themes soon to spice things up. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;There are endless possibilities to what we could build with well structured schema driven content.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Fork the &lt;a href="https://github.com/flexdinesh/keystone-static-blog" rel="noopener noreferrer"&gt;template&lt;/a&gt; and make it yours. That's what I did for &lt;a href="https://dineshpandiyan.com/" rel="noopener noreferrer"&gt;my website&lt;/a&gt; as well. Say hi to me on &lt;a href="https://twitter.com/flexdinesh" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;. Happy Blogging! 👋&lt;/p&gt;

</description>
      <category>react</category>
      <category>nextjs</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>10-tweet threads are the new micro blogs</title>
      <dc:creator>Dinesh Pandiyan</dc:creator>
      <pubDate>Sat, 05 Oct 2019 06:34:55 +0000</pubDate>
      <link>https://forem.com/flexdinesh/10-tweet-threads-are-the-new-micro-blogs-3p1a</link>
      <guid>https://forem.com/flexdinesh/10-tweet-threads-are-the-new-micro-blogs-3p1a</guid>
      <description>&lt;p&gt;I recently stumbled across Chris's &lt;a class="mentioned-user" href="https://dev.to/chrisachard"&gt;@chrisachard&lt;/a&gt; 10-tweet-thread about React hooks. It was pure genius. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Writing more in less helps stay on point without beating around the bush&lt;/li&gt;
&lt;li&gt;It helps readers consume more knowledge from less content&lt;/li&gt;
&lt;li&gt;Chances of consuming knowledge from a tweet is more when compared to a blog post. &lt;em&gt;(I read at least a 100 tweets a day whereas I only skim around ~10 blog posts a day)&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Blog posts are more suited for in-depth elaborate dissection of a topic. But I personally feel tweet threads make it easy to consume knowledge if the content format is short and a can be summarised.&lt;/p&gt;

&lt;p&gt;In that spirit I am planning to write more tweet threads on short topics that fit into 10 tweets and share it here.&lt;/p&gt;

&lt;p&gt;Here's my first thread — &lt;strong&gt;CSR vs SSR&lt;/strong&gt;&lt;/p&gt;

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

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



&lt;/p&gt;

&lt;p&gt;And second — &lt;strong&gt;React Dev Tools&lt;/strong&gt;&lt;/p&gt;

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

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



&lt;/p&gt;

&lt;p&gt;Would love to hear your thoughts on this.&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>react</category>
    </item>
    <item>
      <title>React Dev Tools — Debug Like a Ninja</title>
      <dc:creator>Dinesh Pandiyan</dc:creator>
      <pubDate>Fri, 20 Sep 2019 06:26:04 +0000</pubDate>
      <link>https://forem.com/flexdinesh/react-dev-tools-debug-like-a-ninja-1l84</link>
      <guid>https://forem.com/flexdinesh/react-dev-tools-debug-like-a-ninja-1l84</guid>
      <description>&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%2Fmiro.medium.com%2Fmax%2F1706%2F1%2AvwNrlUSs4Q2JCsWc9JJsBw.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%2Fmiro.medium.com%2Fmax%2F1706%2F1%2AvwNrlUSs4Q2JCsWc9JJsBw.png" alt="React Dev Tools — Debug Like a Ninja"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Hands down &lt;code&gt;console.log()&lt;/code&gt; was, is and will always be the &lt;strong&gt;greatest debugging tool&lt;/strong&gt; of all time.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;React team launched a &lt;strong&gt;new version&lt;/strong&gt; of the &lt;strong&gt;React Dev Tools&lt;/strong&gt; (v4) a few weeks ago and it’s &lt;strong&gt;fantabulous&lt;/strong&gt;. It gives you &lt;strong&gt;debugging superpowers&lt;/strong&gt; to navigate through your tree, trace data flow, spot weak links and optimise for perf.&lt;br&gt;
There are &lt;strong&gt;two separate tabs (Components, Profiler)&lt;/strong&gt; now as opposed to the old version which had only one React tab.&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%2Fmiro.medium.com%2Fmax%2F2556%2F1%2Aw1hePByhgMPI32PXJxnTgw.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%2Fmiro.medium.com%2Fmax%2F2556%2F1%2Aw1hePByhgMPI32PXJxnTgw.png" alt="Dev Tools"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;New Dev Tools has great many improvements over the old version. To highlight a few —&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🏎 Faster&lt;/li&gt;
&lt;li&gt;🎣 Hooks support&lt;/li&gt;
&lt;li&gt;🧐 Advanced tree filter&lt;/li&gt;
&lt;li&gt;🤠 Better search UX&lt;/li&gt;
&lt;li&gt;👮‍♀️ Badges for HOCs&lt;/li&gt;
&lt;li&gt;🎯 Remembers selection after page reload&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Top 10 Features
&lt;/h1&gt;

&lt;p&gt;Dev Tools can do a lot of things. We’re are not going to dive into the nitty gritty details of each and every feature. Instead we’ll take a look at the &lt;strong&gt;top 10 helpful features&lt;/strong&gt; that help us save time and write better React code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Components Tab
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Live edit state/props&lt;/li&gt;
&lt;li&gt;Search through tree&lt;/li&gt;
&lt;li&gt;Advanced tree filter&lt;/li&gt;
&lt;li&gt;Rendered by&lt;/li&gt;
&lt;li&gt;Owners tree&lt;/li&gt;
&lt;li&gt;Quick Navigation&lt;/li&gt;
&lt;li&gt;Utils&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Profiler Tab
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Profile initial mount&lt;/li&gt;
&lt;li&gt;Why did this render?&lt;/li&gt;
&lt;li&gt;Rendered at&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  1. Live Edit State/Props
&lt;/h1&gt;

&lt;p&gt;You can live edit the state and props of a component and instantly see the results in the page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F1280%2F1%2A3RLDGKkZngCzfuxi61kA5Q.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%2Fmiro.medium.com%2Fmax%2F1280%2F1%2A3RLDGKkZngCzfuxi61kA5Q.gif" alt="live edit state/props"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since the gif is broken, you can view it &lt;a href="https://miro.medium.com/max/1280/1*3RLDGKkZngCzfuxi61kA5Q.gif" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  2. Search Through Tree
&lt;/h1&gt;

&lt;p&gt;The search bar comes handy when you want to quickly find and jump to a component in the tree. Now that the &lt;strong&gt;Dev Tools can remember stuff&lt;/strong&gt; the &lt;strong&gt;selected node is saved between reloads&lt;/strong&gt; and you don’t have to repeat the action every time you reload the page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F1280%2F1%2A0c1JIHJRs1N9RX2JBa4BKA.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%2Fmiro.medium.com%2Fmax%2F1280%2F1%2A0c1JIHJRs1N9RX2JBa4BKA.gif" alt="search through tree"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  3. Component Filter
&lt;/h1&gt;

&lt;p&gt;Filter is a powerful feature to reduce visual noise and focus on what matters at the moment in a huge tree. You can filter with a variety of options like context, HOCs, host(DOM) nodes, component name, etc.&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%2Fmiro.medium.com%2Fmax%2F1280%2F1%2AChPXzBXxRTPXkElTgVSZ9Q.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%2Fmiro.medium.com%2Fmax%2F1280%2F1%2AChPXzBXxRTPXkElTgVSZ9Q.gif" alt="component filter"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  4. Rendered By
&lt;/h1&gt;

&lt;p&gt;Rendered By is a list of &lt;strong&gt;owner/parent components&lt;/strong&gt; that rendered the selected component. You can quickly jump to a parent component by selecting an entry from the list.&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%2Fmiro.medium.com%2Fmax%2F1280%2F1%2AekuimJhLzc1JxDLNLY0jlQ.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%2Fmiro.medium.com%2Fmax%2F1280%2F1%2AekuimJhLzc1JxDLNLY0jlQ.gif" alt="rendered by"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  5. Owners Tree
&lt;/h1&gt;

&lt;p&gt;Owners Tree gives you an &lt;strong&gt;eagle eye view&lt;/strong&gt; of a particular component and its sub-tree and hides the rest of the component tree.&lt;/p&gt;

&lt;p&gt;To enter into owners tree view, you have to &lt;strong&gt;double click&lt;/strong&gt; a component in the tree.&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%2Fmiro.medium.com%2Fmax%2F1280%2F1%2A8-rJTh6j0StDr959kAzOkA.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%2Fmiro.medium.com%2Fmax%2F1280%2F1%2A8-rJTh6j0StDr959kAzOkA.gif" alt="owners tree"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  6. Quick Navigation
&lt;/h1&gt;

&lt;p&gt;Switching between Elements tab (browser dev tools) and Components tab (react dev tools) is pretty easy and the corresponding node is auto selected in these tabs.&lt;/p&gt;

&lt;p&gt;To find the corresponding DOM node in the Elements tab, &lt;strong&gt;select the component and click the small eye icon&lt;/strong&gt; on the right pane. It will take you to the Elements tab with the corresponding DOM node selected.&lt;/p&gt;

&lt;p&gt;To find the corresponding React node from Elements tab, &lt;strong&gt;just select a node in Elements tab and click the Components tab&lt;/strong&gt;. React Dev Tools will automatically select the corresponding component.&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%2Fmiro.medium.com%2Fmax%2F1280%2F1%2AyrboX0DNGA5hzPbwkvyuGA.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%2Fmiro.medium.com%2Fmax%2F1280%2F1%2AyrboX0DNGA5hzPbwkvyuGA.gif" alt="quick navigation"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  7. Utils
&lt;/h1&gt;

&lt;p&gt;Components tab offers three small handy utils.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Suspend component&lt;/li&gt;
&lt;li&gt;Inspect matching DOM element&lt;/li&gt;
&lt;li&gt;Log component data to console&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Suspend Component
&lt;/h2&gt;

&lt;p&gt;You can easily switch between the waiting(loading) and resolved states of &lt;a href="https://reactjs.org/docs/code-splitting.html#reactlazy" rel="noopener noreferrer"&gt;Suspense&lt;/a&gt; components.&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%2Fmiro.medium.com%2Fmax%2F1280%2F1%2AfsayD-RnFvWKAN2WTiJvog.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%2Fmiro.medium.com%2Fmax%2F1280%2F1%2AfsayD-RnFvWKAN2WTiJvog.gif" alt="suspend component"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Inspect matching DOM element
&lt;/h2&gt;

&lt;p&gt;We already saw this in &lt;strong&gt;Quick Navigation&lt;/strong&gt;. You can select a component and click the &lt;strong&gt;eye icon&lt;/strong&gt; on the right pane to jump to its corresponding node in Elements tab to inspect it.&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%2Fmiro.medium.com%2Fmax%2F1280%2F1%2A4vA7mDDmbOdBPyugPfGCBw.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%2Fmiro.medium.com%2Fmax%2F1280%2F1%2A4vA7mDDmbOdBPyugPfGCBw.gif" alt="inspect element"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Log component data to console
&lt;/h2&gt;

&lt;p&gt;This is a handy utility that logs the entire component meta data to the console. The log gives details like props, hooks, corresponding DOM nodes and the component file location in the file system.&lt;/p&gt;

&lt;p&gt;To log component data to console, select the component and &lt;strong&gt;click the small bug icon in the right pane&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F1280%2F1%2A7oO2We69Q6vjUUiprakBMA.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%2Fmiro.medium.com%2Fmax%2F1280%2F1%2A7oO2We69Q6vjUUiprakBMA.gif" alt="log component data"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Intro to Profiler
&lt;/h1&gt;

&lt;p&gt;The next three features are going to be about the Profiler. Before diving into them, we’ll take a look at how profiler works and the key metrics that are used during profiling.&lt;/p&gt;

&lt;p&gt;Every profiling session has two important colour graphs —&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Commit graph&lt;/li&gt;
&lt;li&gt;Component graph&lt;/li&gt;
&lt;/ol&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%2Fmiro.medium.com%2Fmax%2F2878%2F1%2As8gkGYrZfDHDyyiep6-_0w.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%2Fmiro.medium.com%2Fmax%2F2878%2F1%2As8gkGYrZfDHDyyiep6-_0w.png" alt="intro to profiler"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Commit Graph
&lt;/h2&gt;

&lt;p&gt;Commit graph gives you a list of commits (whole tree) during the session. &lt;strong&gt;Each commit denotes a user activity or side-effect that triggered a render in the tree&lt;/strong&gt;.You will see it coloured from orange to green with &lt;strong&gt;orange&lt;/strong&gt; denoting the &lt;strong&gt;costliest&lt;/strong&gt; (took most time) commit and &lt;strong&gt;green&lt;/strong&gt; denoting the &lt;strong&gt;cheapest&lt;/strong&gt; (took least time) commit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Component Graph
&lt;/h2&gt;

&lt;p&gt;Component graph gives you info on which component rendered &lt;strong&gt;during a single commit&lt;/strong&gt;. You can select each commit from the commit graph to see its component graph. It is also colour coded with cost information —&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Dark Grey&lt;/strong&gt; — Component did &lt;strong&gt;not&lt;/strong&gt; render but a &lt;strong&gt;part of its sub-tree&lt;/strong&gt; rendered&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transparent Grey&lt;/strong&gt; — &lt;strong&gt;Neither&lt;/strong&gt; the component nor its subtree rendered&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Coloured&lt;/strong&gt; — &lt;strong&gt;Both&lt;/strong&gt; the component and its entire subtree rendered&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  8. Profile Initial Mount
&lt;/h1&gt;

&lt;p&gt;Initial mount and first render is usually a perf heavy op in most React applications. In older versions of Dev Tools, it was impossible to profile and audit the initial mount phase. But the latest version provides an option to profile and audit the initial mount.&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%2Fmiro.medium.com%2Fmax%2F1280%2F1%2AlPsmgceapwprlUyIyHPArg.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%2Fmiro.medium.com%2Fmax%2F1280%2F1%2AlPsmgceapwprlUyIyHPArg.gif" alt="profile initial mount"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  9. Why Did This Render?
&lt;/h1&gt;

&lt;p&gt;This is the most powerful feature of the Dev Tools yet and it tells you why a component rendered during a profiling session. A component might have rendered for one or many of these reasons —&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Props changed&lt;/li&gt;
&lt;li&gt;State changed&lt;/li&gt;
&lt;li&gt;Hooks changed&lt;/li&gt;
&lt;li&gt;Parent component rendered&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can profile an activity and see why a component rendered during the profiling session &lt;strong&gt;to spot weak links&lt;/strong&gt; and prevent unnecessary renders in the tree.&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%2Fmiro.medium.com%2Fmax%2F1280%2F1%2AoTZB8QDpdg65pRVSvKMgyA.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%2Fmiro.medium.com%2Fmax%2F1280%2F1%2AoTZB8QDpdg65pRVSvKMgyA.gif" alt="why did this render"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: You have check this setting — “Record why each component rendered while profiling” to see this info in the right pane of the Profiler Tab.&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  10. Rendered At
&lt;/h1&gt;

&lt;p&gt;Rendered At info on the right pane gives you two time metrics —&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;When exactly the component rendered during the profiling session&lt;/li&gt;
&lt;li&gt;How long it took for the component to render during the profiling session&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Eg. &lt;strong&gt;2.2s for 2.3ms&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here, &lt;strong&gt;2.2s&lt;/strong&gt; means the component rendered at &lt;strong&gt;2.2s(2200ms)&lt;/strong&gt; from the start of the profiling session which is &lt;strong&gt;0s&lt;/strong&gt;. And &lt;strong&gt;2.3ms&lt;/strong&gt; is the amount of time it took for the component to render.&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%2Fmiro.medium.com%2Fmax%2F1280%2F1%2AYzgMYR2S7lSRsAZLgESoWA.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%2Fmiro.medium.com%2Fmax%2F1280%2F1%2AYzgMYR2S7lSRsAZLgESoWA.gif" alt="rendered at"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since the gif is broken, you can view it &lt;a href="https://miro.medium.com/max/1280/1*YzgMYR2S7lSRsAZLgESoWA.gif" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;That’s all folks.&lt;/p&gt;

&lt;p&gt;If you’re interested in exploring all the features, the React team has built a site just for that — &lt;a href="https://react-devtools-tutorial.now.sh" rel="noopener noreferrer"&gt;Interactive Dev Tools&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Play around with Dev Tools and drop a comment below or &lt;a href="https://twitter.com/intent/tweet/?text=Hey%20@flexdinesh%20I%20read%20your%20post%20about%20@reactjs%20Dev%20Tools%20" rel="noopener noreferrer"&gt;start a conversation&lt;/a&gt; with the author in &lt;a href="https://twitter.com/intent/tweet/?text=Hey%20@flexdinesh%20I%20read%20your%20post%20about%20@reactjs%20Dev%20Tools%20" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; on what you think.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This post is a cross post of the original post from &lt;a href="https://medium.com/the-thinkmill/react-dev-tools-debug-like-a-ninja-c3a5d09895c6" rel="noopener noreferrer"&gt;The Thinkmill Blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>tutorial</category>
      <category>webdev</category>
    </item>
    <item>
      <title>React — Access custom params in handlers the right way</title>
      <dc:creator>Dinesh Pandiyan</dc:creator>
      <pubDate>Thu, 06 Jun 2019 14:13:57 +0000</pubDate>
      <link>https://forem.com/flexdinesh/react-access-custom-params-in-handlers-the-right-way-44mi</link>
      <guid>https://forem.com/flexdinesh/react-access-custom-params-in-handlers-the-right-way-44mi</guid>
      <description>&lt;p&gt;&lt;em&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; - You can access render time values in handlers without having to use anonymous arrow functions. Hint — leverage data attributes.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;React is great in so many aspects and it gives us the freedom to do things in different ways by being less opinionated (or non opinionated).&lt;/p&gt;

&lt;p&gt;In recent days, especially after the release of hooks, the community has been fussing around a lot on &lt;strong&gt;reference equality&lt;/strong&gt; and how anonymous arrow functions in renders are not good for performance.&lt;/p&gt;

&lt;p&gt;We are not going to deep dive into why or how using arrow functions during render affects the performance (or it does not). Here are two contrasting tweets for context.&lt;/p&gt;

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

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



&lt;/p&gt;

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

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



&lt;/p&gt;

&lt;h1&gt;
  
  
  When
&lt;/h1&gt;

&lt;p&gt;We mostly use arrow functions during render only to pass custom parameters to event handlers.&lt;/p&gt;

&lt;p&gt;For example, this is a common use case —&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MyComp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleClick&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// handle click and use id to update state&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// id comes from state&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&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;handleClick&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Hello World!&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  How
&lt;/h1&gt;

&lt;p&gt;Instead of using an anonymous arrow function here, we could leverage &lt;strong&gt;data attributes&lt;/strong&gt; and access values from the event object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MyComp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleClick&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
    &lt;span class="c1"&gt;// handle click and use id to update state&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// id comes from state&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;data-id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleClick&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Hello World!&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Advantages
&lt;/h1&gt;

&lt;p&gt;This approach has a lot of advantages —&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can memoize the callback pretty easily if needed.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MyComp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// id comes from state&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleClick&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
    &lt;span class="c1"&gt;// handle click and use id to update state&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;data-id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleClick&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Hello World!&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;You don't create a new function during component render which saves additional computation cost during vdom diffing.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If child components that use &lt;strong&gt;React.memo&lt;/strong&gt; take this handler as a prop, you don't have to write custom &lt;em&gt;areEqual&lt;/em&gt; method to circumvent &lt;strong&gt;referential integrity&lt;/strong&gt; issues with anon arrow functions.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Keep in mind that all data attributes return String values even if you pass in other primitive types. So it's a good practice to coerce your value while extracting from dataset.&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="c1"&gt;// this will be String&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// convert to type if needed&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I have created a codesandbox to demonstrate how it works — &lt;a href="https://codesandbox.io/s/customparamsinhandlers-9wlls?fontsize=14&amp;amp;hidenavigation=1&amp;amp;module=%2Fsrc%2FTodoList.js&amp;amp;view=editor"&gt;Custom Params In Handlers&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You are amazing! Have a great day! ⚡️&lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>tutorial</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Cache Busting a React App</title>
      <dc:creator>Dinesh Pandiyan</dc:creator>
      <pubDate>Sun, 14 Apr 2019 13:59:25 +0000</pubDate>
      <link>https://forem.com/flexdinesh/cache-busting-a-react-app-22lk</link>
      <guid>https://forem.com/flexdinesh/cache-busting-a-react-app-22lk</guid>
      <description>&lt;p&gt;&lt;em&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; - &lt;a href="https://docs.npmjs.com/about-semantic-versioning"&gt;SemVer&lt;/a&gt; your app and generate a &lt;code&gt;meta.json&lt;/code&gt; file on each build that won't be cached by the browser. Invalidate cache and hard reload the app when there's a version mismatch.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Note: The examples and explanations in this post are React based. But the strategy will work with any web application/framework.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As great as caching is — cache invalidation has been a struggle for a long time now. &lt;strong&gt;Invalidating&lt;/strong&gt; the &lt;strong&gt;cache&lt;/strong&gt; of a web app that's loaded in the browser &lt;strong&gt;is hard&lt;/strong&gt;. But &lt;strong&gt;invalidating&lt;/strong&gt; the &lt;strong&gt;cache&lt;/strong&gt; of a web app that's saved to the &lt;strong&gt;home screen&lt;/strong&gt; is &lt;strong&gt;even harder&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;A quick intro to caching —&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Server caching:&lt;/strong&gt; Web servers cache the resources when they are requested for the first time. Second time onwards, the resources are served from the server cache. There's a lot more to this — CDN, origin servers, edge servers, etc but we'll not go into all that. Invalidating server cache is quite straight forward as we have control over our server and on each new deploy, we could either automatically or manually clear the old cache.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Browser caching:&lt;/strong&gt; Browsers also cache the resources in their own way. When a site is loaded for the first time in the user's browser, the browser decides to cache some resources (mostly assets like images, js and css) locally and the next time the user visits the same site, the browser serves the resources from the local cache. Since we don't have control over the user's browser, clearing cache in the user's browser has always been a bit of a struggle in the past. With cache headers and with build tools like webpack generating unique chunks on each build, it's a becoming a bit easier to manage, but still, it's not without pitfalls.&lt;/p&gt;

&lt;p&gt;Here are some of the gotchas with browser caching —&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Browsers&lt;/strong&gt; tend to &lt;strong&gt;ignore cache validation&lt;/strong&gt; some times if the site is refreshed in the &lt;strong&gt;same tab&lt;/strong&gt; — if the user pins the tab, there's a good chance the site will be loaded from browser cache even if the server cache is cleared. &lt;/li&gt;
&lt;li&gt;If your app is registering a &lt;strong&gt;service-worker&lt;/strong&gt;, then the service worker &lt;strong&gt;cache&lt;/strong&gt; will be &lt;strong&gt;invalidated&lt;/strong&gt; only if the user opens the site in a &lt;strong&gt;new tab&lt;/strong&gt;. The user will be stuck with the service worker cache forever if the tab is never closed.&lt;/li&gt;
&lt;li&gt;If the user &lt;strong&gt;adds&lt;/strong&gt; the site to &lt;strong&gt;home screen&lt;/strong&gt; in mobile/tablet, then the browser &lt;strong&gt;cache&lt;/strong&gt; will be &lt;strong&gt;invalidated&lt;/strong&gt; only if the user explicitly &lt;strong&gt;quits the app&lt;/strong&gt; — it's almost the same as having the same tab open in the browser. I know people who don't quit their home screen apps for months.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Ideally, caching helps to load the site faster. Disabling cache is not the answer. It's also not reliable as you cannot control the behavior of your user's browser. We want to figure out a way to clear the browser or service worker cache every time a new version of our app is deployed to the server.&lt;/p&gt;

&lt;p&gt;A simple yet effective approach&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.npmjs.com/about-semantic-versioning"&gt;SemVer&lt;/a&gt; your deploys&lt;/li&gt;
&lt;li&gt;Bundle the app version into the app&lt;/li&gt;
&lt;li&gt;Generate a &lt;code&gt;meta.json&lt;/code&gt; file with the app version on each build&lt;/li&gt;
&lt;li&gt;Fetch &lt;code&gt;meta.json&lt;/code&gt; on load and compare versions&lt;/li&gt;
&lt;li&gt;Force clear cache and hard reload when there's a version mismatch&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://docs.npmjs.com/about-semantic-versioning"&gt;SemVer&lt;/a&gt; your deploys
&lt;/h2&gt;

&lt;p&gt;Version all your deploys with &lt;a href="https://docs.npmjs.com/about-semantic-versioning"&gt;SemVer&lt;/a&gt;. I personally use these three npm commands that automatically increments the package version and creates a git commit along with a corresponding version tag.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;npm version patch&lt;/code&gt; — &lt;em&gt;for releases with only bug fixes&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;npm version minor&lt;/code&gt; — &lt;em&gt;for releases with new features w/ or w/o bug fixes&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;npm version major&lt;/code&gt; — &lt;em&gt;for major releases or breaking features&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Remember to push your commit with &lt;code&gt;--tag&lt;/code&gt; attribute — &lt;code&gt;git push origin master --tags&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Bundle the app version into the app
&lt;/h2&gt;

&lt;p&gt;Parse the package version during webpack build (or relevant build tool) and set a global variable in the app so you can conveniently check the version in the browser console as well as use this to compare with the latest version.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;packageJson&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;{root-dir}/package.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appVersion&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;packageJson&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once this is set, you will be able to check the app version in the browser console by typing &lt;code&gt;appVersion&lt;/code&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  Generate a &lt;code&gt;meta.json&lt;/code&gt; file with the app version on each build
&lt;/h2&gt;

&lt;p&gt;Run a script to generate a &lt;code&gt;meta.json&lt;/code&gt; file in the &lt;code&gt;public&lt;/code&gt; dir of your app. &lt;/p&gt;

&lt;p&gt;Add a &lt;code&gt;prebuild&lt;/code&gt; npm script that will generate the &lt;code&gt;meta.json&lt;/code&gt; file before each &lt;code&gt;build&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="cm"&gt;/* package.json */&lt;/span&gt;

&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;scripts&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;generate-build-version&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;node generate-build-version&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;prebuild&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;npm run generate-build-version&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="c1"&gt;// other scripts&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* generate-build-version.js */&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;packageJson&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./package.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;appVersion&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;packageJson&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;jsonData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;appVersion&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;jsonContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;jsonData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;writeFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./public/meta.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;jsonContent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;utf8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;An error occured while writing JSON Object to meta.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="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="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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;meta.json file has been saved with latest version number&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After each build, once you deploy the app, &lt;code&gt;meta.json&lt;/code&gt; can be accessed using the path &lt;code&gt;/meta.json&lt;/code&gt; and you can fetch the json like a REST endpoint. It won't be cached by the browser as browsers don't cache XHR requests. So you will always get the latest &lt;code&gt;meta.json&lt;/code&gt; file even if your bundle files are cached.&lt;/p&gt;

&lt;p&gt;So if the &lt;code&gt;appVersion&lt;/code&gt; in your bundle file is less than the &lt;code&gt;version&lt;/code&gt; in &lt;code&gt;meta.json&lt;/code&gt;, then we know that the &lt;strong&gt;browser cache is stale and we will need to invalidate it&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You can use this script to compare semantic versions —&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// version from `meta.json` - first param&lt;/span&gt;
&lt;span class="c1"&gt;// version in bundle file - second param&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;semverGreaterThan&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;versionA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;versionB&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;versionsA&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;versionA&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;/g&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;versionsB&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;versionB&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;/g&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;versionsA&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;versionsB&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;versionsA&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shift&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;versionsB&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shift&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="c1"&gt;// eslint-disable-next-line no-continue&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;a&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;// eslint-disable-next-line no-restricted-globals&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;isNaN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="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;You can also find this code in my &lt;a href="https://github.com/flexdinesh/cache-busting-example/blob/ad03c264e2f52c71609726104e38ea3593520e07/src/CacheBuster.js#L6"&gt;GitHub example&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Fetch &lt;code&gt;meta.json&lt;/code&gt; on load and compare versions
&lt;/h2&gt;

&lt;p&gt;When the &lt;code&gt;App&lt;/code&gt; is mounted, fetch &lt;code&gt;meta.json&lt;/code&gt; and compare the current version with the latest version in the server.&lt;/p&gt;

&lt;p&gt;When there is a &lt;strong&gt;version mismatch&lt;/strong&gt; =&amp;gt; force &lt;strong&gt;clear cache&lt;/strong&gt; and hard reload&lt;br&gt;
When the versions are the same =&amp;gt; Render the rest of the app&lt;/p&gt;

&lt;p&gt;I have built a &lt;code&gt;CacheBuster&lt;/code&gt; component that will force clear cache and reload the site. The logic will work for most of the sites but can be tweaked for custom cases depending on the applications.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* CacheBuster component */&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;packageJson&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../package.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appVersion&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;packageJson&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;semverGreaterThan&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;versionA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;versionB&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;// code from above snippet goes here&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;CacheBuster&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Component&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;isLatestVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;refreshCacheAndReload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Clearing cache and hard reloading...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;caches&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="c1"&gt;// Service worker cache should be cleared with caches.delete()&lt;/span&gt;
          &lt;span class="nx"&gt;caches&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&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="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;names&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;names&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;caches&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="nx"&gt;name&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="c1"&gt;// delete browser cache and hard reload&lt;/span&gt;
        &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;componentDidMount&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/meta.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&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="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;latestVersion&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;currentVersion&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appVersion&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;shouldForceRefresh&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;semverGreaterThan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;latestVersion&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;currentVersion&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;shouldForceRefresh&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`We have a new version - &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;latestVersion&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;. Should force refresh`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;isLatestVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&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="s2"&gt;`You already have the latest version - &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;latestVersion&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;. No cache refresh needed.`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;isLatestVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isLatestVersion&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;refreshCacheAndReload&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isLatestVersion&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;refreshCacheAndReload&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;And we can use this &lt;code&gt;CacheBuster&lt;/code&gt; component to control the render in &lt;code&gt;App&lt;/code&gt; component&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* App component */&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;CacheBuster&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isLatestVersion&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;refreshCacheAndReload&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;loading&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isLatestVersion&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// You can decide how and when you want to force reload&lt;/span&gt;
            &lt;span class="nx"&gt;refreshCacheAndReload&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;

          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"App"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;header&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"App-header"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Cache Busting - Example&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                  Bundle version - &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;code&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;v&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appVersion&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;code&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;header&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;CacheBuster&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also find the code for both these components here —&lt;/p&gt;

&lt;p&gt;CacheBuster - &lt;a href="https://github.com/flexdinesh/cache-busting-example/blob/master/src/CacheBuster.js"&gt;CacheBuster.js&lt;/a&gt;&lt;br&gt;
App - &lt;a href="https://github.com/flexdinesh/cache-busting-example/blob/master/src/App.js"&gt;App.js&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Force clear cache and hard reload when there's a version mismatch
&lt;/h2&gt;

&lt;p&gt;Every time the app is loaded, we check for the latest version. Depending on whether the app version is stale or not, we can decide to clear cache in different ways.&lt;/p&gt;

&lt;p&gt;For instance, &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can hard-reload before rendering the app&lt;/li&gt;
&lt;li&gt;You can show a modal/popup asking the user to click a button and trigger a hard-reload&lt;/li&gt;
&lt;li&gt;You can hard-reload when the app is idle&lt;/li&gt;
&lt;li&gt;You can hard-reload after a few seconds with &lt;code&gt;setTimeout()&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can find the entire code from this post with a working example in this repo — &lt;a href="https://github.com/flexdinesh/cache-busting-example"&gt;cache-busting-example&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's all folks. If you have any feedback for this approach (good and bad), do let me know in the comments.&lt;/p&gt;

&lt;p&gt;Cache busting is fun. 🎉&lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>browser</category>
    </item>
    <item>
      <title>React Hooks: Test custom hooks with Enzyme</title>
      <dc:creator>Dinesh Pandiyan</dc:creator>
      <pubDate>Sat, 23 Mar 2019 11:47:08 +0000</pubDate>
      <link>https://forem.com/flexdinesh/react-hooks-test-custom-hooks-with-enzyme-40ib</link>
      <guid>https://forem.com/flexdinesh/react-hooks-test-custom-hooks-with-enzyme-40ib</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; - Wrap your custom hook in a component and shallow render it to test implementation details.&lt;/p&gt;

&lt;h1&gt;
  
  
  What you will learn
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;React test strategies

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;user observable behaviour&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;implementation details&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Testing custom hooks with Enzyme&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Test Strategies
&lt;/h2&gt;

&lt;p&gt;There are broadly two strategies to test our React codebase.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Testing user observable behaviour&lt;/li&gt;
&lt;li&gt;Testing implementation details&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Testing user observable behaviour
&lt;/h3&gt;

&lt;p&gt;Testing user observable behaviour means writing tests against components that test&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;how the component is rendered&lt;/li&gt;
&lt;li&gt;how the component is re-rendered when user interacts with the DOM&lt;/li&gt;
&lt;li&gt;how props/state control what is rendered&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Consider the following component - &lt;code&gt;Greet&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;Greet&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;User&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setName&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;setName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Pinocchio&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Hello, &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;!&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Testing the user observable behaviour in &lt;code&gt;Greet&lt;/code&gt; would mean&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;test if &lt;code&gt;Greet&lt;/code&gt; is rendered without crashing&lt;/li&gt;
&lt;li&gt;test if &lt;code&gt;Hello, User!&lt;/code&gt; is rendered when user prop is not passed&lt;/li&gt;
&lt;li&gt;test if &lt;code&gt;Hello, Bruce!&lt;/code&gt; is rendered when &lt;code&gt;Bruce&lt;/code&gt; is passed as value to &lt;code&gt;user&lt;/code&gt; prop&lt;/li&gt;
&lt;li&gt;test if the text changes to &lt;code&gt;Hello, Pinocchio!&lt;/code&gt; when the user clicks on the element&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Testing implementation details
&lt;/h3&gt;

&lt;p&gt;Testing implementation details means writing tests against state logic that test&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;how the state is initialized with default/prop values&lt;/li&gt;
&lt;li&gt;how the state changes when handlers are invoked&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Consider the same component - &lt;code&gt;Greet&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;Greet&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;User&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setName&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;setName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Pinocchio&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Hello, &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;!&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Testing implementation details in &lt;code&gt;Greet&lt;/code&gt; would mean&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;test if &lt;code&gt;name&lt;/code&gt; is set to default value &lt;code&gt;User&lt;/code&gt; when user prop is not passed to &lt;code&gt;Greet&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;test if &lt;code&gt;name&lt;/code&gt; is set to prop value when user prop is passed to &lt;code&gt;Greet&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;test if &lt;code&gt;name&lt;/code&gt; is updated when &lt;code&gt;setName&lt;/code&gt; is invoked&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Testing custom hooks with Enzyme
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Note: Please make sure your React version is &lt;code&gt;^16.8.5&lt;/code&gt;. Hooks won't re-render components with enzyme shallow render in previous versions and the React team fixed it in this release. If your React version is below that, you might have to use enzyme mount and &lt;code&gt;.update()&lt;/code&gt; your wrapper after each change to test the re-render.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Testing implementation details might seem unnecessary and might even be considered as a bad practice when you are writing tests against components that contains presentational (UI) logic and render elements to the DOM. But &lt;strong&gt;custom hooks&lt;/strong&gt; contain only &lt;strong&gt;state logic&lt;/strong&gt; and it is imperative that we test the implementation details thoroughly so we know exactly how our custom hook will behave within a component.&lt;/p&gt;

&lt;p&gt;Let's write a custom hook to update and validate a form field.&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="cm"&gt;/* useFormField.js */&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;useFormField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;initialVal&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setVal&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;initialVal&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;isValid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setValid&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;setVal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;setValid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isValid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;setValid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isValid&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;As great as custom hooks are in abstracting away re-usable logic in our code, they do have one limitation. Even though they are just JavaScript functions they will work only inside React components. You cannot just invoke them and write tests against what a hook returns. You have to wrap them inside a React component and test the values that it returns.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;custom hooks cannot be tested like JavaScript functions&lt;/li&gt;
&lt;li&gt;custom hooks should be wrapped inside a React component to test its behaviour&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thanks to the composibility of hooks, we could pass a hook as a prop to a component and everything will work exactly as how it's supposed to work. We can write a wrapper component to render and test our hook.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* useFormField.test.js */&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;HookWrapper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hook&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hook&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hook&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;hook&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;hook&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can access the hook like a JavaScript object and test its behaviour.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* useFormField.test.js */&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;shallow&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;enzyme&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;useFormField&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./useFormField&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;HookWrapper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hook&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hook&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hook&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;hook&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;hook&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should set init value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;wrapper&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;shallow&lt;/span&gt;&lt;span class="p"&gt;(&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;HookWrapper&lt;/span&gt; &lt;span class="na"&gt;hook&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;useFormField&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="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;hook&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;div&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isValid&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;hook&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;wrapper&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;shallow&lt;/span&gt;&lt;span class="p"&gt;(&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;HookWrapper&lt;/span&gt; &lt;span class="na"&gt;hook&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;useFormField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;marco&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// destructuring objects - {} should be inside brackets - () to avoid syntax error&lt;/span&gt;
  &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;hook&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;div&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isValid&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;hook&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;marco&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The full test suite for &lt;code&gt;useFormField&lt;/code&gt; custom hook will look like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* useFormField.test.js */&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;shallow&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;enzyme&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;useFormField&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./useFormField&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;HookWrapper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hook&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hook&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hook&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;hook&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;hook&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;useFormField&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should render&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;wrapper&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;shallow&lt;/span&gt;&lt;span class="p"&gt;(&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;HookWrapper&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nx"&gt;toBeTruthy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should set init value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;wrapper&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;shallow&lt;/span&gt;&lt;span class="p"&gt;(&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;HookWrapper&lt;/span&gt; &lt;span class="na"&gt;hook&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;useFormField&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="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;hook&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;div&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isValid&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;hook&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;wrapper&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;shallow&lt;/span&gt;&lt;span class="p"&gt;(&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;HookWrapper&lt;/span&gt; &lt;span class="na"&gt;hook&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;useFormField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;marco&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// destructuring objects - {} should be inside brackets - () to avoid syntax error&lt;/span&gt;
    &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;hook&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;div&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isValid&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;hook&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;marco&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should set the right val value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;wrapper&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;shallow&lt;/span&gt;&lt;span class="p"&gt;(&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;HookWrapper&lt;/span&gt; &lt;span class="na"&gt;hook&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;useFormField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;marco&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;hook&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;div&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isValid&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;hook&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;marco&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;polo&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="nx"&gt;hook&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;div&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isValid&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;hook&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;polo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should set the right isValid value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;wrapper&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;shallow&lt;/span&gt;&lt;span class="p"&gt;(&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;HookWrapper&lt;/span&gt; &lt;span class="na"&gt;hook&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;useFormField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;marco&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;hook&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;div&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isValid&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;hook&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;marco&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isValid&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;polo&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="nx"&gt;hook&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;div&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isValid&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;hook&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;polo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isValid&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;value&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;span class="nx"&gt;hook&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;div&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isValid&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;hook&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isValid&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rendering the custom hook and accessing it as a prop should give us full access to its return values.&lt;/p&gt;

&lt;p&gt;If you're using &lt;code&gt;useEffect&lt;/code&gt; hook in your custom hook, make sure you wrap the &lt;code&gt;shallow&lt;/code&gt; or &lt;code&gt;mount&lt;/code&gt; call with &lt;a href="https://reactjs.org/docs/test-utils.html#act"&gt;ReactTestUtils.act()&lt;/a&gt; to have the effects flushed out before assertions. Enzyme might support this internally soon but for now, this is required. More info on this here - &lt;a href="https://reactjs.org/docs/hooks-faq.html#how-to-test-components-that-use-hooks"&gt;hooks-faq&lt;/a&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;act&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;wrapper&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;shallow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HookWrapper&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All code snippets in this post can be found in the repo - &lt;a href="https://github.com/flexdinesh/testing-hooks"&gt;testing-hooks&lt;/a&gt; with a working example.&lt;/p&gt;

&lt;p&gt;Happy testing! 🎉&lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>tutorial</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Don't build your portfolio with just React</title>
      <dc:creator>Dinesh Pandiyan</dc:creator>
      <pubDate>Fri, 01 Feb 2019 07:03:32 +0000</pubDate>
      <link>https://forem.com/flexdinesh/dont-build-your-portfolio-with-just-react-11a9</link>
      <guid>https://forem.com/flexdinesh/dont-build-your-portfolio-with-just-react-11a9</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; - Use React along with &lt;a href="https://www.gatsbyjs.org/"&gt;Gatsby&lt;/a&gt; or &lt;a href="https://nextjs.org/"&gt;Next&lt;/a&gt; to build static public sites.&lt;/p&gt;

&lt;p&gt;React is all the rage these days and IMHO developing websites feels a lot easier with React. Of course, you can use and favor other frameworks too but I'm gonna limit the context of this post to React affictionados.&lt;/p&gt;

&lt;h1&gt;
  
  
  Why you should use React
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Component based&lt;/li&gt;
&lt;li&gt;Pre-configured (for most part) starter with &lt;a href="https://facebook.github.io/create-react-app/"&gt;create-react-app&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Faster development&lt;/li&gt;
&lt;li&gt;React is AWESOME 🎉&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Why you shouldn't use just React
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;DOM is constructed in the browser (client-side)&lt;/li&gt;
&lt;li&gt;SEO implications&lt;/li&gt;
&lt;li&gt;Higher &lt;a href="https://developers.google.com/web/tools/lighthouse/audits/time-to-interactive"&gt;Time to Interactive (TTI)&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's get a little more into why all these things happen.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;React loads the site into DOM after this line is invoked&lt;/strong&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;ReactDOM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What this means it, the whole DOM, styles and behavior of the site is initialized only after the &lt;code&gt;bundle.js&lt;/code&gt; is loaded and executed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you turn off JavaScript in your browser settings and open a React site, nothing will load and you will see only an empty page&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Unfortunately that's how &lt;strong&gt;search engines&lt;/strong&gt; look at your site during &lt;strong&gt;indexing&lt;/strong&gt;. So if you create your site with just React, only your page title will be indexed and nothing else inside your React code will be indexed.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Edit: Google executes JavaScript during indexing to a limited extent, meaning if you don't have content in first render or if render takes more than N secs, it skips the content. Also DuckDuckGo is becoming the de-facto search engine to a lot of folks in recent times and it's been my only search engine for more than a year now. And DuckDuckGo doesn't execute JavaScript during indexing. So &lt;strong&gt;relying on search engines to execute JavaScript is not a safe bet&lt;/strong&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Other than SEO implications, loading your site without the DOM in the first load (paint) increases your TTI metric. If you could load the DOM in the first load before loading your scripts, you'll drastically improve the UX.&lt;/p&gt;

&lt;h1&gt;
  
  
  SSR and Static Site Generation for the Rescue
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;SSR Websites&lt;/strong&gt; and &lt;strong&gt;Static Websites&lt;/strong&gt; are two different things. SSR pages are rendered from a server on each request where as static pages are generated during build time. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you use frameworks like &lt;a href="https://www.gatsbyjs.org/"&gt;Gatsby&lt;/a&gt; or &lt;a href="https://nextjs.org/"&gt;Next&lt;/a&gt;, you can either generate static sites or create SSR pages.&lt;/p&gt;

&lt;p&gt;In other words, if you turn off your JavaScript and load these sites, the whole DOM will be loaded as the pages are already rendered.&lt;/p&gt;

&lt;p&gt;Ideally, only event handlers and complementary data should be loaded and attached after the first render. &lt;/p&gt;

&lt;h1&gt;
  
  
  Gatsby or Next?
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;If you are building a static public website, Gatsby will be the right tool.&lt;/li&gt;
&lt;li&gt;If you are building a website that has a login and a lot of server side logic, Next will be the right tool.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Of course there are a lot other options and reasons to choose your right tool but personally these two will be enough for most preliminary requirements.&lt;/p&gt;

&lt;p&gt;Other points to argue&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can create SSR pages with React but you will need to setup your server and everything manually. Next.js makes this whole process easier.&lt;/li&gt;
&lt;li&gt;Google claims that it executes JavaScript recently while indexing, but I tried and it didn't work for me. We're not sure of the nuances of it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Gatsby is ❤️
&lt;/h2&gt;

&lt;p&gt;In other news, I'm totally smitten and unfathomably in love with &lt;a href="https://www.gatsbyjs.org/"&gt;Gatsby&lt;/a&gt; ❤️&lt;/p&gt;

&lt;p&gt;I migrated my &lt;a href="https://portfoliov2.dineshpandiyan.com"&gt;old portfolio&lt;/a&gt; from React to Gatsby exactly for the reasons discussed in this post. This is my new portfolio in Gatsby - &lt;a href="https://dineshpandiyan.com"&gt;Dinesh Pandiyan&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Going forward, I'm planning to build all my side projects in Gatsby this year. I created a Gatsby starter boilerplate with added configurations over default Gatsby starter - &lt;a href="https://github.com/flexdinesh/gatsby-boilerplate"&gt;Gatsby Boilerplate&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;My Gatsby based sites so far&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dineshpandiyan.com"&gt;Dinesh Pandiyan&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tweetfancy.io"&gt;tweetfancy.io&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Another pet project in WIP stage. I'll probably launch it in a month.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You are amazing! ✨&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>react</category>
      <category>portfolio</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Deploy Gatsby sites to GitHub Pages</title>
      <dc:creator>Dinesh Pandiyan</dc:creator>
      <pubDate>Wed, 23 Jan 2019 15:11:42 +0000</pubDate>
      <link>https://forem.com/flexdinesh/deploy-gatsby-sites-to-github-pages-eed</link>
      <guid>https://forem.com/flexdinesh/deploy-gatsby-sites-to-github-pages-eed</guid>
      <description>&lt;p&gt;&lt;a href="https://www.gatsbyjs.org/"&gt;Gatsby&lt;/a&gt; is all the rage now, thanks to the amazing team behind the fancy and easy-to-use framework. I'm not going to go into the details of why you &lt;strong&gt;should use Gatsby&lt;/strong&gt; if you're &lt;strong&gt;building a static website&lt;/strong&gt;. There are a ton of posts out there that say just that.&lt;/p&gt;

&lt;p&gt;Here's an excellent article on why - &lt;a href="https://medium.freecodecamp.org/why-you-should-use-gatsbyjs-to-build-static-sites-4f90eb6d1a7b"&gt;Why you should use GatsbyJS to build static sites&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These are my top reasons&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Amazing Dev Experience&lt;/li&gt;
&lt;li&gt;Pre-baked optimizations&lt;/li&gt;
&lt;li&gt;Generated site is super fast&lt;/li&gt;
&lt;li&gt;Out of the box support for GraphQL&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Deploy Gatsby sites anywhere
&lt;/h2&gt;

&lt;p&gt;Since Gatsby generates static files, you can deploy it anywhere.&lt;br&gt;
But the easiest and quickest option would be to deploy your site to &lt;a href="https://pages.github.com/"&gt;GitHub Pages&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  GitHub Pages
&lt;/h3&gt;

&lt;p&gt;You can host static websites for free with GitHub Pages. You can have two types of websites hosted in GitHub Pages.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Personal Website - Repo name should be &lt;code&gt;username.github.io&lt;/code&gt; and the site can be accessed at &lt;code&gt;https://username.github.io&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Project Website - Repo name can be anything and the site can be accessed at &lt;code&gt;https://username.github.io/repo-name&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;All you gotta do is to push the static site to the repo with &lt;code&gt;index.html&lt;/code&gt; at the root dir.&lt;/p&gt;

&lt;p&gt;More info here - &lt;a href="https://pages.github.com/"&gt;GitHub Pages&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploy Gatsby sites to GitHub Pages
&lt;/h2&gt;

&lt;p&gt;Gatsby generates your static site when you run the command &lt;code&gt;gatsby build&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Only catch here is that GitHub Pages expect your site files to be in the root dir but Gatsby generates the site files in a dir called &lt;code&gt;public&lt;/code&gt;. So we cannot host the source code and the public files in the same repo. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.npmjs.com/package/gh-pages"&gt;gh-pages&lt;/a&gt; is a really nice package that helps you push static sites to a GitHub repo from anywhere.&lt;/p&gt;

&lt;h3&gt;
  
  
  Steps
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;yarn add gh-pages --dev&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Create a new dir &lt;code&gt;scripts&lt;/code&gt; at project root and create a file &lt;code&gt;deploy-github.js&lt;/code&gt; in &lt;code&gt;scripts&lt;/code&gt; dir&lt;/li&gt;
&lt;li&gt;Add this code to &lt;code&gt;deploy-github.js&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;ghpages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gh-pages&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// replace with your repo url&lt;/span&gt;
&lt;span class="nx"&gt;ghpages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;public&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;branch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;master&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://github.com/flexdinesh/flexdinesh.github.io.git&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="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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Deploy Complete!&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;ul&gt;
&lt;li&gt;Add a new npm script &lt;code&gt;deploy:github&lt;/code&gt; in &lt;code&gt;package.json&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"deploy:github"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npm run build &amp;amp;&amp;amp; node ./scripts/deploy-github"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Run the script and your static site will be up and running in a few seconds&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Note: When you run the command, you will be asked to enter your GitHub creds in the command line before publish.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If you need further materials, you could take a look at the source code of my personal website &lt;a href="https://github.com/flexdinesh/portfolio"&gt;Dinesh Pandiyan - Portfolio&lt;/a&gt;. It is deployed to both Netlify and Github Pages.&lt;/p&gt;

&lt;p&gt;If you're looking for a good Gatsby starter template, I maintain a boilerplate with my pre-baked web setup - &lt;a href="https://github.com/flexdinesh/gatsby-boilerplate"&gt;Gatsby Boilerplate&lt;/a&gt;. I usually fork this repo if I want to start with a new Gatsby site.&lt;/p&gt;

&lt;p&gt;Happy Gatsbying! 🔥&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>gatsby</category>
      <category>react</category>
    </item>
    <item>
      <title>Tweet in bold, italics and strikethrough text</title>
      <dc:creator>Dinesh Pandiyan</dc:creator>
      <pubDate>Tue, 22 Jan 2019 16:37:19 +0000</pubDate>
      <link>https://forem.com/flexdinesh/tweet-in-bold-italics-and-strikethough-text-3b1b</link>
      <guid>https://forem.com/flexdinesh/tweet-in-bold-italics-and-strikethough-text-3b1b</guid>
      <description>&lt;p&gt;&lt;em&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; - I built a tool - &lt;a href="https://tweetfancy.io/"&gt;tweetfancy.io&lt;/a&gt; to tweet in bold, italics and strikethrough text.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Ever fancied tweeting with &lt;strong&gt;bold&lt;/strong&gt;, &lt;em&gt;italics&lt;/em&gt; or &lt;del&gt;strikethrough&lt;/del&gt; text? &lt;/p&gt;

&lt;p&gt;Twitter doesn't allow its users to format tweets. But having an option to &lt;em&gt;emphasize&lt;/em&gt; or &lt;strong&gt;call out&lt;/strong&gt; a word or a phrase in a tweet might come in handy at times.&lt;/p&gt;

&lt;h1&gt;
  
  
  Announcing tweetfancy.io
&lt;/h1&gt;

&lt;p&gt;I recently realized that the UTF charset has &lt;a href="https://en.wikipedia.org/wiki/Mathematical_Alphanumeric_Symbols"&gt;characters and symbols&lt;/a&gt; that look like &lt;strong&gt;bold&lt;/strong&gt;, &lt;em&gt;italics&lt;/em&gt; or &lt;del&gt;strikethrough&lt;/del&gt; ascii characters. So I built a tool that generates these characters which could be copied and pasted into almost any website out there.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Write your original text in markdown&lt;/li&gt;
&lt;li&gt;The editor will generate text for you to copy/paste&lt;/li&gt;
&lt;li&gt;Tweet directly from the editor&lt;/li&gt;
&lt;li&gt;Copy/Paste the generated text into any website (FB/Insta/etc)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Note: The generated characters are mathematical symbols and not actual English alphabets. So if you run them by a screen reader, you'll find that the reader reads them out by their symbol name. But then, all screen readers struggle reading smileys too (🎉🔥🚀🦄😎) and that doesn't stop us from using smileys. So,we might use this as well.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Here's the link - &lt;a href="https://tweetfancy.io/"&gt;tweetfancy.io&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Go bonkers tweeting in &lt;strong&gt;bold&lt;/strong&gt;, &lt;em&gt;italics&lt;/em&gt; or &lt;del&gt;strikethrough&lt;/del&gt;! Happy tweeting! 🎉&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>2018 - Year In Review</title>
      <dc:creator>Dinesh Pandiyan</dc:creator>
      <pubDate>Fri, 04 Jan 2019 16:34:40 +0000</pubDate>
      <link>https://forem.com/flexdinesh/2018---year-in-review-515h</link>
      <guid>https://forem.com/flexdinesh/2018---year-in-review-515h</guid>
      <description>&lt;p&gt;2018 has been an amazing year for me. I &lt;strong&gt;learnt&lt;/strong&gt; new things, &lt;strong&gt;built&lt;/strong&gt; a few things, &lt;strong&gt;wrote&lt;/strong&gt; a handful of blog posts and &lt;strong&gt;met&lt;/strong&gt; some amazing devs in the community. But above all, I became a better dev by &lt;em&gt;learning and reflecting on my mistakes&lt;/em&gt; throughout. Here's a quick list down of how 2018 turned out to be an amazing year for me.&lt;/p&gt;

&lt;h1&gt;
  
  
  Things I built in 2018
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/flexdinesh/browser-or-node"&gt;Browser or Node&lt;/a&gt; - &lt;em&gt;January&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/flexdinesh/typy"&gt;Typy&lt;/a&gt; - &lt;em&gt;February&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/flexdinesh/dev-landing-page"&gt;Dev Landing Page&lt;/a&gt; - &lt;em&gt;March&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/flexdinesh/axios-retry-interceptor"&gt;Axios Retry Interceptor&lt;/a&gt; - &lt;em&gt;March&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/flexdinesh/react-redux-boilerplate"&gt;React-Redux Boilerplate&lt;/a&gt; - &lt;em&gt;March&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/flexdinesh/react-render-in-browser"&gt;React Render-In-Browser&lt;/a&gt; - &lt;em&gt;May&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/flexdinesh/react-socks"&gt;React Socks&lt;/a&gt; - &lt;em&gt;November&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/flexdinesh/typy/blob/master/CHANGELOG.md#300-9-dec-2018"&gt;Typy v3&lt;/a&gt; - &lt;em&gt;December&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/flexdinesh/react-socks/blob/master/CHANGELOG.md#100-9-dec-2018"&gt;React Socks Stable&lt;/a&gt; - &lt;em&gt;December&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Things I wrote in 2018
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dev.to/flexdinesh/how-i-got-started-with-open-source--882"&gt;How I got started with Open-Source&lt;/a&gt; - &lt;em&gt;February&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/flexdinesh/accessing-nested-objects-in-javascript--9m4"&gt;Accessing Nested Objects in JavaScript&lt;/a&gt; - &lt;em&gt;February&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/flexdinesh/short-circuit-assignment-in-javascript--4k80"&gt;Short Circuit Assignment in JavaScript&lt;/a&gt; - &lt;em&gt;February&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/flexdinesh/publish-your-own-npm-package---5b71"&gt;Publish your own NPM package&lt;/a&gt; - &lt;em&gt;March&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/flexdinesh/create-your-developer-landing-page-with-github-pages---42jk"&gt;Create your developer landing page with GitHub Pages&lt;/a&gt; - &lt;em&gt;March&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/flexdinesh/react-redux-boilerplate-with-best-practices--2pp5"&gt;React-Redux Boilerplate with Best Practices&lt;/a&gt; - &lt;em&gt;March&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/flexdinesh/upgrade-to-webpack-4---5bc5"&gt;Upgrade to Webpack 4&lt;/a&gt; - &lt;em&gt;March&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/flexdinesh/i-re-wrote-my-portfolio-and-added-some-magic-22n7"&gt;I Created My Portfolio with React and Some Magic&lt;/a&gt; - &lt;em&gt;April&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/flexdinesh/render-browser-specific-content-with-react--amm"&gt;Render Browser Specific Content with React&lt;/a&gt; - &lt;em&gt;May&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/flexdinesh/introducing-responsive-react-components--1a6a"&gt;Introducing Responsive React Components&lt;/a&gt; - &lt;em&gt;November&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Goal Stats
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;✅ Start with OSS &lt;em&gt;(have over 10 actively maintained projects now)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;✅ Become an active contributor to a repo that’s not mine &lt;em&gt;(became one of the top contributors)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;✅ Create and publish a library that others find helpful &lt;em&gt;(have 7 libs now with 130k+ downloads)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;✅ Build a portfolio&lt;/li&gt;
&lt;li&gt;✅ Hit 1k reps in Stack Overflow &lt;em&gt;(2.4k currently)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;✅ Hit 1k followers in dev.to &lt;em&gt;(2.2k currently)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;✅ Learn Docker, ELK stack, reverse proxy&lt;/li&gt;
&lt;li&gt;⚠️ Hit 700 followers in twitter &lt;em&gt;(695 now)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;❌ Build a website that others find helpful&lt;/li&gt;
&lt;li&gt;❌ Learn GraphQL, serverless, web components&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Started with &lt;strong&gt;OSS&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Quit my awesome job to take on a &lt;strong&gt;challenging&lt;/strong&gt; role&lt;/li&gt;
&lt;li&gt;Moved to &lt;strong&gt;new country&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Started contributing more in &lt;strong&gt;Stack Overflow&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Started building and &lt;strong&gt;publishing libraries&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Started &lt;strong&gt;writing&lt;/strong&gt; about the things I learn and the things I build&lt;/li&gt;
&lt;li&gt;Became a publisher in &lt;strong&gt;Hackernoon&lt;/strong&gt; and &lt;strong&gt;Codeburst&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;All my write ups together have been viewed over &lt;strong&gt;250k times&lt;/strong&gt; (100k+ DEV and 150k+ Medium)&lt;/li&gt;
&lt;li&gt;A few blog posts that I wrote got featured in other platforms and curations&lt;/li&gt;
&lt;li&gt;Found awesome people in the community (twitter and dev.to) and drew &lt;strong&gt;inspiration&lt;/strong&gt; from them&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Dev principles I picked up in 2018
&lt;/h1&gt;

&lt;p&gt;I want to wrap up this post by highlighting a few dev principles I learnt and strictly adopted in my workflow in 2018.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Code &lt;strong&gt;without tests&lt;/strong&gt; is as good as &lt;strong&gt;no code at all&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No&lt;/strong&gt; Abstraction &lt;strong&gt;&amp;gt;&lt;/strong&gt; &lt;strong&gt;Wrong&lt;/strong&gt; Abstraction&lt;/li&gt;
&lt;li&gt;Most optimization strategies you come up with will either result in &lt;strong&gt;premature-optimization&lt;/strong&gt; or &lt;strong&gt;over-optimization&lt;/strong&gt;. &lt;strong&gt;Optimize only when absolutely necessary&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Have a broader vision of the problem/solution but break it down and build in &lt;strong&gt;small portions&lt;/strong&gt; incrementally&lt;/li&gt;
&lt;li&gt;If you add a todo to improve later, you’ll never get back to it. So, &lt;strong&gt;do it now&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;🥂 To another wonderful year full of opportunities and amazing people 🥂&lt;/p&gt;

</description>
      <category>yearinreview</category>
      <category>webdev</category>
      <category>career</category>
    </item>
    <item>
      <title>2018: Year In Review</title>
      <dc:creator>Dinesh Pandiyan</dc:creator>
      <pubDate>Tue, 18 Dec 2018 01:30:08 +0000</pubDate>
      <link>https://forem.com/flexdinesh/2018-year-in-review-4agf</link>
      <guid>https://forem.com/flexdinesh/2018-year-in-review-4agf</guid>
      <description>&lt;p&gt;We are 12 days away from the end of another phenomenal year. I am going to write a &lt;strong&gt;Year In Review&lt;/strong&gt; post in a week and tag it with &lt;code&gt;#yearinreview&lt;/code&gt; hashtag. I encourage everyone else to do so as well. It will give you an opportunity to look back at what this year has brought for you and set some realistic goals for next year.&lt;/p&gt;

&lt;p&gt;I will keep my post limited to this context but it can be anything you want to write about.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;career progress&lt;/li&gt;
&lt;li&gt;goals set&lt;/li&gt;
&lt;li&gt;goals smashed&lt;/li&gt;
&lt;li&gt;goals overdue&lt;/li&gt;
&lt;li&gt;new things learnt&lt;/li&gt;
&lt;li&gt;new achievements unlocked&lt;/li&gt;
&lt;li&gt;old things taken care of&lt;/li&gt;
&lt;li&gt;anything that made you happy&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's all about growing together.&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>webdev</category>
      <category>yearinreview</category>
    </item>
    <item>
      <title>What is the role of a developer advocate?</title>
      <dc:creator>Dinesh Pandiyan</dc:creator>
      <pubDate>Sat, 24 Nov 2018 11:40:34 +0000</pubDate>
      <link>https://forem.com/flexdinesh/what-is-the-role-of-a-developer-advocate-3ce0</link>
      <guid>https://forem.com/flexdinesh/what-is-the-role-of-a-developer-advocate-3ce0</guid>
      <description>&lt;p&gt;A few of high profile speakers in conferences and big companies have the designation "Developer Advocate".&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What exactly does a Developer Advocate do?&lt;/li&gt;
&lt;li&gt;Do they have a specific job description or is it an umbrella term for a wide range of roles?&lt;/li&gt;
&lt;li&gt;Is someone in the Dev community a Developer Advocate or know someone who is one?&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>discuss</category>
    </item>
  </channel>
</rss>
