<?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: Joel Warrington</title>
    <description>The latest articles on Forem by Joel Warrington (@joelzwarrington).</description>
    <link>https://forem.com/joelzwarrington</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%2F764483%2Fb8bf406b-e086-4c0f-bf21-bcafc0181378.jpg</url>
      <title>Forem: Joel Warrington</title>
      <link>https://forem.com/joelzwarrington</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/joelzwarrington"/>
    <language>en</language>
    <item>
      <title>Playing around with Hotwire ⚡️</title>
      <dc:creator>Joel Warrington</dc:creator>
      <pubDate>Wed, 05 Jun 2024 21:17:15 +0000</pubDate>
      <link>https://forem.com/joelzwarrington/playing-around-with-hotwire-2a2a</link>
      <guid>https://forem.com/joelzwarrington/playing-around-with-hotwire-2a2a</guid>
      <description>&lt;p&gt;Hey 👋,&lt;/p&gt;

&lt;p&gt;Let's jump into my bi-weekly update in the &lt;a href="https://dev.to/joelzwarrington/series/27288"&gt;ramen profitability series&lt;/a&gt;, where I'm sharing progress on my latest project: &lt;a href="https://homesuiteapartment.com"&gt;HomeSuiteApartment&lt;/a&gt;, a tool to manage rental properties.&lt;/p&gt;

&lt;p&gt;In this update, I've been focused on the workflow to inquire about units and scheduling viewings, and I touched up the subscription page. Given my current schedule (working fulltime with a 1 year old), I'm pretty happy with what I was able to accomplish in these past two weeks, and am looking forward to setting a open-beta launch date in the next month or two.&lt;/p&gt;

&lt;h2&gt;
  
  
  Updating the listing page
&lt;/h2&gt;

&lt;p&gt;I updated the listing page, and have since added a form to submit inquiries and additional messages on-top of the inquiry.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0rgdao1xlgc02xhb7sa9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0rgdao1xlgc02xhb7sa9.png" alt="Screenshot of a unit listing with inquiry form at the bottom" width="800" height="1027"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Once the inquiry is submitted, the property manager will be able to respond in their inbox, and schedule a viewing.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjuykdvafzkrp8fhwfa0a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjuykdvafzkrp8fhwfa0a.png" alt="Screenshot showing list of inquiries" width="800" height="540"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;As you can see in the example above, I'm using &lt;a href="https://turbo.hotwired.dev/"&gt;Turbo Frames&lt;/a&gt; to give a single-page application feel,  while not having to write any JavaScript.&lt;/p&gt;

&lt;p&gt;Here's a boiled down example of how you can accomplish a view that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erb"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;link_to&lt;/span&gt; &lt;span class="n"&gt;inquiry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;data: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;turbo_frame: &lt;/span&gt;&lt;span class="s2"&gt;"inquiry"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt; 

&lt;span class="c"&gt;&amp;lt;%# this is a placeholder for the selected inquiry %&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;turbo_frame_tag&lt;/span&gt; &lt;span class="s2"&gt;"inquiry"&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, in your &lt;code&gt;#show&lt;/code&gt; action, you can simply wrap your item in the turbo frame tag, and instead of following the redirect, Turbo will replace the frame with id &lt;code&gt;inquiry&lt;/code&gt; from the response.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erb"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;turbo_frame_tag&lt;/span&gt; &lt;span class="s2"&gt;"inquiry"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;turbo_frame_tag&lt;/span&gt; &lt;span class="n"&gt;inquiry&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;This is an inquiry!&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A major benefit to this, is that if you want to implement single-page application-like features, all you need to do is use a few custom HTML elements, and some data-attributes. So if you're a Ruby on Rails developer, you really won't need to do much to decompose your views into frames.&lt;/p&gt;

&lt;p&gt;You can use &lt;a href="https://turbo.hotwired.dev/handbook/frames"&gt;Turbo Frames&lt;/a&gt; on form submission using &lt;a href="https://turbo.hotwired.dev/handbook/streams"&gt;Turbo Streams&lt;/a&gt;. &lt;a href="https://turbo.hotwired.dev/handbook/streams"&gt;Turbo Streams&lt;/a&gt; allow you to modify &lt;a href="https://turbo.hotwired.dev/handbook/frames"&gt;Turbo Frames&lt;/a&gt; very precisely with these actions: &lt;em&gt;append&lt;/em&gt;, &lt;em&gt;prepend&lt;/em&gt;, (insert) &lt;em&gt;before&lt;/em&gt;, (insert) &lt;em&gt;after&lt;/em&gt;, &lt;em&gt;replace&lt;/em&gt;, &lt;em&gt;update&lt;/em&gt;, &lt;em&gt;remove&lt;/em&gt;, and &lt;em&gt;refresh&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;In my example, when I submit a new message to the inquiry, I'm appending the message to the list, and also am clearing out the message form. Similar to decomposing with &lt;a href="https://turbo.hotwired.dev/handbook/frames"&gt;Turbo Frames&lt;/a&gt;, you're only ever sprinkling in things as needed, and you don't need to add any boilerplate up-front.&lt;/p&gt;

&lt;p&gt;Most of your controllers still function as normal Ruby on Rails controllers using redirects, but when you need the extra functionality, or want to speed up page loads, you can use &lt;a href="https://turbo.hotwired.dev/handbook/streams"&gt;Turbo Streams&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I highly recommend checking out &lt;a href="https://hotwired.dev/"&gt;Hotwire&lt;/a&gt;, it's really been a breath of fresh air for me. You get to do a lot more with less JavaScript, and allows you to get the same benefits as other frameworks/libraries (such as react) without having to significantly change your development process.&lt;/p&gt;

&lt;p&gt;If you're interested, you should have a look at the &lt;a href="https://www.hotrails.dev/"&gt;Hotrails tutorial&lt;/a&gt; which goes over all of the concepts introduced by Turbo and Stimulus.&lt;/p&gt;

&lt;h2&gt;
  
  
  Subscriptions
&lt;/h2&gt;

&lt;p&gt;In the last update, I got subscriptions working, and integrated with &lt;a href="https://stripe.com"&gt;Stripe&lt;/a&gt;. I'm using the &lt;a href="https://github.com/pay-rails/pay"&gt;Pay gem&lt;/a&gt; to manage the subscriptions, as it provides a lot of built-in functionality, and the &lt;a href="https://github.com/stripe/stripe-ruby"&gt;Stripe Ruby Client&lt;/a&gt; for other API calls not supported with the &lt;a href="https://github.com/pay-rails/pay"&gt;Pay gem&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this update, I went ahead and fleshed out the page with pricing and features. So we started with this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj7h9v74bwh36nxtlm1lv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj7h9v74bwh36nxtlm1lv.png" alt="Subscription page before the redesign" width="800" height="532"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;and, ended with this:&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fffefg9mm38szupfy5ynz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fffefg9mm38szupfy5ynz.png" alt="Subscription page after the redesign" width="800" height="833"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I won't take credit for the re-design though. If you're using the &lt;a href="https://tailwindcss.com/"&gt;Tailwind CSS Library&lt;/a&gt; you should checkout &lt;a href="https://tailwindui.com/"&gt;Tailwind UI&lt;/a&gt;. It's helped me scaffold a few components and pages quite easily, without having a designer onboard.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next!
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Further improving the viewing + inquiry pages&lt;/li&gt;
&lt;li&gt;Improve the unit to listing workflow&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;See you in two weeks!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>rails</category>
    </item>
    <item>
      <title>Building a Ruby on Rails MVP.</title>
      <dc:creator>Joel Warrington</dc:creator>
      <pubDate>Fri, 24 May 2024 15:18:44 +0000</pubDate>
      <link>https://forem.com/joelzwarrington/building-an-ruby-on-rails-mvp-4eoo</link>
      <guid>https://forem.com/joelzwarrington/building-an-ruby-on-rails-mvp-4eoo</guid>
      <description>&lt;p&gt;Hey there 👋,&lt;/p&gt;

&lt;p&gt;It's been 2 weeks since I announced the new project I'm working on: &lt;a href="https://homesuiteapartment.com"&gt;HomeSuiteApartment&lt;/a&gt;, a tool to manage properties.&lt;/p&gt;

&lt;p&gt;Today, I'll be sharing an update update on the progress so far, what's next, and share some developer insights for your own Ruby on Rails project! If you'd prefer, I've also uploaded a &lt;a href="https://youtu.be/3Q--P6-dtO4"&gt;video walking through the product&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Progress made so far
&lt;/h2&gt;

&lt;p&gt;I've been able to build out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;an integration with Stripe to offer subscription plans&lt;/li&gt;
&lt;li&gt;CRUD routes for buildings and units&lt;/li&gt;
&lt;li&gt;CRUD routes to list a unit (at the moment only on HomeSuiteApartment, eventually on other rental listing marketplaces) and send inquiries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A lot of the pages have placeholder content at the moment, which I'll be fleshing out, but the intended workflow is almost done.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpdn4d8dukwg8w6vuot2t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpdn4d8dukwg8w6vuot2t.png" alt="Subscription overview page" width="800" height="532"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffsbn0cx2m24xscjjk70m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffsbn0cx2m24xscjjk70m.png" alt="Buildings index page" width="800" height="532"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foi69euyx0djyj0cccyzg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foi69euyx0djyj0cccyzg.png" alt="New building page" width="800" height="532"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fszy7zexj90qd6e6gjr2c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fszy7zexj90qd6e6gjr2c.png" alt="New building page with validation errors" width="800" height="532"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxzhf8xmmjp7iqecxb4gl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxzhf8xmmjp7iqecxb4gl.png" alt="Unit show page" width="800" height="532"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fic9wfo8bvwr7d4n71eg5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fic9wfo8bvwr7d4n71eg5.png" alt="Unit listing show page, from the admin" width="800" height="532"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcppe0g3rszaf86d9fwfo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcppe0g3rszaf86d9fwfo.png" alt="Unit listing show page, viewed by the public" width="800" height="532"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F12uttwoxhuucxhcs1qq8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F12uttwoxhuucxhcs1qq8.png" alt="New inquiry page" width="800" height="532"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  An overview on my workflow
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://rubyonrails.org/"&gt;Ruby on Rails&lt;/a&gt;, in my opinion, is the most productive full-stack web framework to-date.&lt;/p&gt;

&lt;p&gt;I'd highly recommend reading up on the &lt;a href="https://rubyonrails.org/doctrine"&gt;Rails Doctrine&lt;/a&gt;, it really explains why Ruby on Rails came to be what it is today and how it's surpassed other frameworks.&lt;/p&gt;




&lt;p&gt;To get started with Ruby on Rails, it was as simple as generating a new project, using the &lt;code&gt;rails&lt;/code&gt; command line.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gem &lt;span class="nb"&gt;install &lt;/span&gt;rails
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rails new homesuiteapartment
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;from there, it's as simple as using the provided &lt;a href="https://guides.rubyonrails.org/generators.html"&gt;generators&lt;/a&gt; to scaffold the app.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bin/rails generate scaffold building name
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This generates a database migration, model, route definition, controller and views, as well as tests if you've configured that. In all of a few minutes, you've got a 'working' application. In most cases, you'll need to do more, but the scaffold generator alone seems like cheating.&lt;/p&gt;

&lt;p&gt;One of my favourite things about using generators, is that it's quite easy to customize, especially if you've implemented other gems for authorization and need to change the controller template.&lt;/p&gt;




&lt;p&gt;Thankfully, &lt;a href="https://www.ruby-lang.org/en/"&gt;Ruby&lt;/a&gt; and &lt;a href="https://rubyonrails.org/"&gt;Ruby on Rails&lt;/a&gt; are well established, and have a large community building 'packages', known as &lt;a href="https://guides.rubygems.org/what-is-a-gem/"&gt;gems&lt;/a&gt;, similar to node packages.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://rubyonrails.org/"&gt;Ruby on Rails&lt;/a&gt; is really just a collection of Gems which work very well together, such as &lt;a href="https://github.com/rails/rails/tree/main/activerecord"&gt;ActiveRecord&lt;/a&gt;, &lt;a href="https://github.com/rails/rails/tree/main/activemodel"&gt;ActiveModel&lt;/a&gt;, &lt;a href="https://github.com/rails/rails/tree/main/actionview"&gt;ActionView&lt;/a&gt; and much more.&lt;/p&gt;

&lt;p&gt;Gems are a helpful tool to easily add new functionality to your Ruby on Rails application, and is especially helpful when building an MVP so you don't need to build everything from scratch.&lt;/p&gt;

&lt;h3&gt;
  
  
  Some of my favourite gems
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Authentication
&lt;/h4&gt;

&lt;p&gt;As mentioned in my previous blog post, &lt;a href="https://dev.to/joelzwarrington/on-the-road-to-ramen-profitability-21fc"&gt;On the road to ramen profitability 🍜 💸&lt;/a&gt;, I mentioned that I'm using &lt;a href="https://github.com/heartcombo/devise"&gt;devise&lt;/a&gt;. It's one of the most popular open-source authentication solutions for Rails.&lt;/p&gt;

&lt;p&gt;I highly recommend it, as it's very configurable, and there are many plugins which I'll implement in the future, such as &lt;a href="https://github.com/omniauth/omniauth"&gt;OmniAuth&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;One pitfall though, if you've never worked with Ruby on Rails, I recommend avoiding it and starting with a simple authentication system from scratch.&lt;/p&gt;

&lt;h4&gt;
  
  
  Authorization
&lt;/h4&gt;

&lt;p&gt;If you're unfamiliar with authorization, it's very different from authentication. Read the &lt;a href="https://auth0.com/docs/get-started/identity-fundamentals/authentication-and-authorization"&gt;Authentication vs. Authorization article&lt;/a&gt; from auth0 to learn more, but in essence:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;authentication is the process of verifying who a user is, while authorization is the process of verifying what they have access to.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There are a few gems which implement different strategies, such as &lt;a href="https://github.com/CanCanCommunity/cancancan"&gt;CanCanCan&lt;/a&gt; and &lt;a href="https://github.com/varvet/pundit"&gt;Pundit&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;My favourite gem for implementing authorization strategies is &lt;a href="https://github.com/palkan/action_policy"&gt;ActionPolicy&lt;/a&gt;. It's very similar to &lt;a href="https://github.com/varvet/pundit"&gt;Pundit&lt;/a&gt;, but is more extensible and isn't as barebones.&lt;/p&gt;

&lt;p&gt;It's as simple as adding a new policy, and implementing the methods corresponding to the actions in your controller. In the example below, we have the &lt;code&gt;UnitPolicy&lt;/code&gt; which will be used in the &lt;code&gt;UnitsController&lt;/code&gt;. The &lt;code&gt;organization_user?&lt;/code&gt; is a method which will return &lt;code&gt;true&lt;/code&gt; if the user is part of the organization they're trying to access.&lt;/p&gt;

&lt;p&gt;At the moment, most of my policies are very simple and simply check that a user is part of an organization, however, in the future it'll be easy to add permissions, roles, etc.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UnitPolicy&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationPolicy&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index?&lt;/span&gt;
    &lt;span class="n"&gt;organization_user?&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;new?&lt;/span&gt;
    &lt;span class="n"&gt;organization_user?&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create?&lt;/span&gt;
    &lt;span class="n"&gt;organization_user?&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;show?&lt;/span&gt;
    &lt;span class="n"&gt;organization_user?&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;edit?&lt;/span&gt;
    &lt;span class="n"&gt;organization_user?&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;update?&lt;/span&gt;
    &lt;span class="n"&gt;organization_user?&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;destroy?&lt;/span&gt;
    &lt;span class="n"&gt;organization_user?&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="kp"&gt;private&lt;/span&gt;

  &lt;span class="n"&gt;relation_scope&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;relation&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;relation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;organization: &lt;/span&gt;&lt;span class="n"&gt;organization&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;params_filter&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;permit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="ss"&gt;:name&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Views and Components
&lt;/h4&gt;

&lt;p&gt;Out of the box, Ruby on Rails uses &lt;a href="https://guides.rubyonrails.org/layouts_and_rendering.html"&gt;erb templating&lt;/a&gt; to build views, and &lt;a href="https://guides.rubyonrails.org/layouts_and_rendering.html#using-partials"&gt;partials&lt;/a&gt;. In Rails convention over configuration fashion, it's best to have views which correspond to your &lt;code&gt;get&lt;/code&gt; actions, and you'll see these views get generated when you run the scaffold generator.&lt;/p&gt;

&lt;p&gt;However, you might want to re-use and share code between views, and at first most would reach for &lt;a href="https://guides.rubyonrails.org/layouts_and_rendering.html#using-partials"&gt;partials&lt;/a&gt;, or if you're brave enough, think that implementing a React frontend will make this better for you, but there's a better solution.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://viewcomponent.org/"&gt;ViewComponent&lt;/a&gt; is a framework/gem for creating reusable, testable &amp;amp; encapsulated view components, built to integrate seamlessly with Ruby on Rails.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/pantographe/view_component-form"&gt;ViewComponent::Form&lt;/a&gt; provides an ActionView FormBuilder, so you can easily use &lt;a href="https://viewcomponent.org/"&gt;ViewComponent&lt;/a&gt; components in your form helpers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I recommend giving these two gems a try, to , and helps with the composability that one might want, &lt;/p&gt;

&lt;p&gt;These gems are both great at reducing the complexity and maintainability of partials, and allows for better composability, something which can be difficult in ERB templating. As mentioned previously, I've seen a lot of people reach for React frontends to solve this problem, and I think it's the wrong approach for a few reasons. If you're interested in that topic, let me know and I can publish an article going in-depth there.&lt;/p&gt;

&lt;h4&gt;
  
  
  Code formatting and linting
&lt;/h4&gt;

&lt;p&gt;I highly recommend adding &lt;a href="https://github.com/standardrb/standard"&gt;standardrb&lt;/a&gt; to your project. Under the hood it uses &lt;a href="https://github.com/rubocop/rubocop"&gt;rubocop&lt;/a&gt; (A Ruby static code analyzer and formatter) and doesn't require any configuration - that's what makes it so powerful.&lt;/p&gt;

&lt;p&gt;When building an MVP, you should spend the least amount of time working on things which don't directly provide value to what you're building. Linting is not a feature of your product.&lt;/p&gt;

&lt;p&gt;When the time comes that I want to be picky about my formatting and linting rules, I'll likely pull out &lt;a href="https://github.com/standardrb/standard"&gt;standard&lt;/a&gt; and write my own &lt;a href="https://github.com/rubocop/rubocop"&gt;https://github.com/rubocop/rubocop&lt;/a&gt; rules, but in the meantime this is more than good enough.&lt;/p&gt;

&lt;h4&gt;
  
  
  Testing
&lt;/h4&gt;

&lt;p&gt;Similar to linting and formatting, testing isn't really a feature. Some would highly argue against shipping code without rock-solid tests. But it really slows you down if you're hunting absolute coverage.&lt;/p&gt;

&lt;p&gt;In my own projects, I'll use &lt;a href="https://rspec.info/"&gt;rspec&lt;/a&gt; with &lt;a href="https://github.com/thoughtbot/shoulda-matchers"&gt;shoulda-matchers&lt;/a&gt; alongside &lt;a href="https://github.com/thoughtbot/factory_bot"&gt;FactoryBot&lt;/a&gt; to quickly and easily write simple tests.&lt;/p&gt;

&lt;p&gt;For the most part, I won't add many more tests than what's included in the basic scaffold generator. Not to say I won't write tests, but covering every code path is not nescessary here. Happy path is good enough.&lt;/p&gt;

&lt;h4&gt;
  
  
  Running jobs
&lt;/h4&gt;

&lt;p&gt;Ruby on Rails provides a common interface for scheduled jobs called &lt;a href="https://guides.rubyonrails.org/active_job_basics.html"&gt;ActiveJob&lt;/a&gt;, but there isn't a single job runner in the scene. There are gems such as &lt;a href="https://github.com/resque/resque"&gt;resque&lt;/a&gt; and &lt;a href="https://github.com/sidekiq/sidekiq"&gt;sidekiq&lt;/a&gt; but both of these gems are dependent on adding Redis.&lt;/p&gt;

&lt;p&gt;These days, I'll be using &lt;a href="https://github.com/basecamp/solid_queue"&gt;solid_queue&lt;/a&gt; as it's a simple solution which uses your existing SQL database.&lt;/p&gt;

&lt;h4&gt;
  
  
  Clean code
&lt;/h4&gt;

&lt;p&gt;To avoid a lot of the boilerplate with &lt;a href="https://rubyonrails.org/"&gt;Ruby on Rails&lt;/a&gt; controllers, I recommend the &lt;a href="https://github.com/heartcombo/responders"&gt;responders&lt;/a&gt; gem. Also it's used by &lt;a href="https://github.com/heartcombo/devise"&gt;devise&lt;/a&gt; under the hood!&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next?
&lt;/h2&gt;

&lt;p&gt;Thanks for sticking to the end, I hope I've shared a few gems that will help you build your own &lt;a href="https://rubyonrails.org/"&gt;Ruby on Rails&lt;/a&gt; application if you decide to do so.&lt;/p&gt;

&lt;p&gt;As for me, I'll continue building out &lt;a href="https://homesuiteapartment.com/"&gt;HomeSuiteApartment&lt;/a&gt;, and in the next 2 weeks will mostly focus on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;polishing pages, such as the subscription overview and unit listing page&lt;/li&gt;
&lt;li&gt;adding functionality to see inquiries, and book viewings from inquiries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;See you in 2 weeks, for the next update!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>rails</category>
      <category>startup</category>
    </item>
    <item>
      <title>On the road to ramen profitability 🍜 💸</title>
      <dc:creator>Joel Warrington</dc:creator>
      <pubDate>Wed, 08 May 2024 23:43:13 +0000</pubDate>
      <link>https://forem.com/joelzwarrington/on-the-road-to-ramen-profitability-21fc</link>
      <guid>https://forem.com/joelzwarrington/on-the-road-to-ramen-profitability-21fc</guid>
      <description>&lt;p&gt;It’s always been a dream of mine to build a kick-ass product. Today I’m starting that product, and will be documenting my progress towards &lt;a href="https://paulgraham.com/ramenprofitable.html"&gt;ramen profitability 🍜&lt;/a&gt;, with a post every 2 weeks to keep myself accountable and to share my learning along the way.&lt;/p&gt;

&lt;p&gt;Let's dive in!&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;Property managers have to track and respond to rental inquiries across a dozen or more sites to find the right tenant.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://homesuiteapartment.com/"&gt;HomeSuiteApartment&lt;/a&gt; helps property managers track and respond to these inquiries so they can find the right tenant quickly, all in one place.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where we're starting
&lt;/h2&gt;

&lt;p&gt;We're starting at the bottom floor, it can only go up from here!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;0$ MRR;&lt;/li&gt;
&lt;li&gt;and 0 customers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It feels incredibly hard to scale something from 0 to 1, and this won't be any different. It's practically a ∞% increase! &lt;/p&gt;

&lt;h2&gt;
  
  
  What's been built so far
&lt;/h2&gt;

&lt;p&gt;A basic marketing site built-on &lt;a href="https://jekyllrb.com/"&gt;Jekyll&lt;/a&gt; and hosted via &lt;a href="https://pages.cloudflare.com/"&gt;Cloudflare Pages&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm8mqy44xlkx8p1iwbstx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm8mqy44xlkx8p1iwbstx.png" alt="Marketing Site Home Page" width="800" height="476"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A 7.1 &lt;a href="https://rubyonrails.org/"&gt;Ruby on Rails&lt;/a&gt; application hosted on a &lt;a href="https://www.hetzner.com/"&gt;Hetzner VPS&lt;/a&gt; and deployed via &lt;a href="https://kamal-deploy.org/"&gt;Kamal&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Users can signup and login via the &lt;a href="https://github.com/heartcombo/devise"&gt;Devise&lt;/a&gt; gem and create their organizations.&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzghlt7a9h2mr4dvh0oat.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzghlt7a9h2mr4dvh0oat.png" alt="Organization create form" width="800" height="531"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At the moment, I’m launching into private beta, and &lt;br&gt;
Until the MVP is launched into open beta, &lt;br&gt;
Until I'm ready to launch an MVP, you won't be able to do much, and instead will be shown a private beta screen, where they can email me with any ideas.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F546lrupd05mpclu28thq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F546lrupd05mpclu28thq.png" alt="Private Beta Screen" width="800" height="531"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Prepare for an MVP launch with minimal feature set, aggressively cutting requirements where possible&lt;/li&gt;
&lt;li&gt;Reach out to more property managers in the area&lt;/li&gt;
&lt;li&gt;Setup basic subscription with Stripe to unlock MRR and get commitment from new customers.

&lt;ul&gt;
&lt;li&gt;This is an area where there will be lots of exploration in the future, but something simple to start will help me understand who is finding value in the product&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;See you in 2 weeks for the next update!&lt;/p&gt;

</description>
      <category>startup</category>
      <category>webdev</category>
      <category>rails</category>
    </item>
    <item>
      <title>Building clean React, with custom hooks 🎣</title>
      <dc:creator>Joel Warrington</dc:creator>
      <pubDate>Fri, 04 Feb 2022 10:58:15 +0000</pubDate>
      <link>https://forem.com/joelzwarrington/building-clean-react-with-custom-hooks-3b3l</link>
      <guid>https://forem.com/joelzwarrington/building-clean-react-with-custom-hooks-3b3l</guid>
      <description>&lt;p&gt;Building maintainable production-ready React components can be a challenge, there are a lot of things to worry about; such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;class or functional components&lt;/li&gt;
&lt;li&gt;sharing state across components&lt;/li&gt;
&lt;li&gt;handling events&lt;/li&gt;
&lt;li&gt;retrieving and modifying data from multiple APIs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of these concerns can lead to a bloated component 😳, spanning multiple event handlers, data fetching, and transformation functions. In this post, I'll explain some of the tools to reduce this complexity in a way that you'll be confident when releasing to production; &lt;strong&gt;hooks&lt;/strong&gt; 🎣.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/l1AsJtaK9CGE8Gn84/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/l1AsJtaK9CGE8Gn84/giphy.gif" alt="Gif of Spongebob Hook with caption 'im going to have some fun'" width="480" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What are React hooks?
&lt;/h2&gt;

&lt;p&gt;React Hooks allow you to &lt;em&gt;hook&lt;/em&gt; into React state and lifecycle features within a functional component. You're able to control when a function is evaluated, when to re-render components, update state, and more.&lt;/p&gt;

&lt;p&gt;React provides a few hooks which can be used on their own or in combination with custom hooks. In this guide, I'll focus on the following base hooks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;useState&lt;/li&gt;
&lt;li&gt;useReducer&lt;/li&gt;
&lt;li&gt;useEffect&lt;/li&gt;
&lt;li&gt;useMemo&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;others include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;useContext &lt;/li&gt;
&lt;li&gt;useCallback&lt;/li&gt;
&lt;li&gt;useRef&lt;/li&gt;
&lt;li&gt;useImperativeHandle&lt;/li&gt;
&lt;li&gt;useLayoutEffect&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can get a detailed explanation about these hooks &lt;a href="https://reactjs.org/docs/hooks-reference.html"&gt;here&lt;/a&gt;, but I'll be explaining them in practice below.&lt;/p&gt;

&lt;h2&gt;
  
  
  Basic hooks in action 🎬
&lt;/h2&gt;

&lt;h3&gt;
  
  
  useState 🗿
&lt;/h3&gt;

&lt;p&gt;useState is likely the most common hook in any functional component. If you'd like your component to have any state and be able to re-render, he's your guy 👉😎👉&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;function&lt;/span&gt; &lt;span class="nf"&gt;MyComponent&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;search&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setSearch&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="nx"&gt;onChange&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="nf"&gt;setSearch&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="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Searching&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;search&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, after each keystroke, our stateful value and UI is updated. If we used a normal variable instead, such as:&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;function&lt;/span&gt; &lt;span class="nf"&gt;MyComponent&lt;/span&gt;&lt;span class="p"&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;search&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="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;
        &lt;span class="nx"&gt;onChange&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="nx"&gt;search&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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;search&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}}&lt;/span&gt;
      &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Searching&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;search&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;You'll notice that the variable is up to date in your console, but doesn't update in the UI. This is the purpose of using &lt;code&gt;useState&lt;/code&gt;, and why hooks are used to hook into React's lifecycle updates.&lt;/p&gt;

&lt;p&gt;Some other things to keep in mind:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;stateful values from &lt;code&gt;useState&lt;/code&gt; are immutable and should only be updated using the provided function&lt;/li&gt;
&lt;li&gt;stateful values can be any type, such as string, char, number, or objects.&lt;/li&gt;
&lt;li&gt;functions which operate on the variables in place won't work, such as &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice"&gt;splice&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  useReducer 🤏
&lt;/h3&gt;

&lt;p&gt;useReducer means what it implies, a way of providing two values to get one back, very similar to the &lt;a href="**https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce**"&gt;reduce&lt;/a&gt; method. You can use it very similarly to the &lt;code&gt;useState&lt;/code&gt; hook, but it handles complex scenarios much more easily.&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;function&lt;/span&gt; &lt;span class="nf"&gt;MyComponent&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;counter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useReducer&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="nx"&gt;action&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;state&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="nx"&gt;onClick&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="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Click&lt;/span&gt; &lt;span class="nx"&gt;me&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Incremented&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;times&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;In this example, after each button click, we're dispatching an action (in this case a simple &lt;code&gt;1&lt;/code&gt; value), which gets passed to our reducer function. The value returned from there will be used to compute the new stateful value. However, it's much more useful when you've got many ways to update state.&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;function&lt;/span&gt; &lt;span class="nf"&gt;MyComponent&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;search&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useReducer&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="nx"&gt;action&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;switch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SET&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;CLEAR&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;CAPITALIZE&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toUpperCase&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="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;
        &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;search&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nx"&gt;onChange&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="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SET&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;payload&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="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="nx"&gt;onClick&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="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;CLEAR&lt;/span&gt;&lt;span class="dl"&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;clear&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&amp;gt;{" "&lt;/span&gt;&lt;span class="err"&gt;}
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="nx"&gt;onClick&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="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;CAPITALIZE&lt;/span&gt;&lt;span class="dl"&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;capitalize&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Searching&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;search&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Some things to keep in mind:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;anything returned from your reducer will be the new stateful value&lt;/li&gt;
&lt;li&gt;the action (second argument in your reducer function) can be any value you choose, but in most cases will be an object with a key of &lt;code&gt;type&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  useEffect 🍹
&lt;/h3&gt;

&lt;p&gt;Implicit in the name of the hook, you're able to calculate side effects. It's particularly useful when trying to evaluate or update something on a state change or re-render.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;MyComponent&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;search&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setSearch&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="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="nf"&gt;useState&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="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;I will log once&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="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;I will log whenever any state or a re-render occurs&lt;/span&gt;&lt;span class="dl"&gt;"&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;search&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;I will log every time search is updated&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;search&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="nx"&gt;search&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="nx"&gt;onChange&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="nf"&gt;setSearch&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="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="nx"&gt;onChange&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="nf"&gt;setName&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="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Some things to keep in mind:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the second argument provided is the dependency array&lt;/li&gt;
&lt;li&gt;a useEffect will re-evaluate whenever its dependencies change&lt;/li&gt;
&lt;li&gt;providing no dependency array will evaluate every re-render/state change.&lt;/li&gt;
&lt;li&gt;providing an empty dependency array will only evaluate once when the component initially re-renders (useful for making initial API requests on component load)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  useMemo 📝
&lt;/h3&gt;

&lt;p&gt;The shape of this hook is very similar to useEffect, but instead of performing side-effects, it's used to perform taxing calculations. The use of this hook can dramatically increase performance when you're doing complex filtering or modification to data.&lt;/p&gt;

&lt;p&gt;Assuming this initial code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;MyComponent&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;movies&lt;/span&gt; &lt;span class="o"&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;My Neighbor Totoro&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;Kiki's Delivery Service&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;The Secret World of Arrietty&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;search&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setSearch&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="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="nf"&gt;useState&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;filteredMovies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;movies&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;movie&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;movie&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;search&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="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filteredMovies&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;movies&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;search&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="nx"&gt;onChange&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="nf"&gt;setSearch&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="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="nx"&gt;onChange&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="nf"&gt;setName&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="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It appears to work just fine with a few movies, but once this list is filtering many items and with much more complex logic, our component will be inefficient. It'll recompute and calculate our filtered movies even when the conditions which impact the filter (search) aren't updated, and instead on each render.&lt;/p&gt;

&lt;p&gt;To solve that, we can filter our movies in a useMemo hook.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;MyComponent&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;movies&lt;/span&gt; &lt;span class="o"&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;My Neighbor Totoro&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;Kiki's Delivery Service&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;The Secret World of Arrietty&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;search&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setSearch&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="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="nf"&gt;useState&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;filteredItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useMemo&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;movies&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;movie&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;movie&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;search&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;search&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;recomputed filteredItems&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;filteredItems&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;filteredItems&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="nx"&gt;onChange&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="nf"&gt;setSearch&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="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="nx"&gt;onChange&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="nf"&gt;setName&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="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now each time we change the search, our list of movies will be filtered, however changing the name won't cause a recalculation, improving the performance of our component!&lt;/p&gt;

&lt;p&gt;In the future, this might not be necessary with some new features in React mentioned at React Conf 2021.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Making your own custom hooks 🙌
&lt;/h2&gt;

&lt;p&gt;Now that you know about some of the foundational hooks, let's look at building our own! Custom hooks can be built out of many or a single base hook, to provide abstracted functions or state.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useDebounce&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use-debounce&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;useMovieFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;movies&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;filter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useReducer&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="nx"&gt;action&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="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;search&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;debouncedFilter&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useDebounce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1000&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;filteredMovies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useMemo&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;filter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;search&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="mi"&gt;0&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;movies&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;movies&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;movie&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;movie&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;debouncedFilter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;search&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;movies&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;debouncedFilter&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;filteredMovies&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;In this example, we're providing our hook with a list of movies, which will be filtered by a search and view filter. In addition, we've improved the performance by taking advantage of a debounce function, and ultimately delaying the re-render of the component until the user pauses typing.&lt;/p&gt;

&lt;p&gt;So what have we gotten so far by adding our own custom hook?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It can be reused anywhere else we'll want to filter and search for movies&lt;/li&gt;
&lt;li&gt;The logic is organized in one place and does not pollute the component definition&lt;/li&gt;
&lt;li&gt;It'll be easier to test, as we won't need to rely on rendering anything!&lt;/li&gt;
&lt;li&gt;this hook is built using another hook, meaning we get ultimately reusability!&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Testing your custom hook 🐛
&lt;/h2&gt;

&lt;p&gt;Testing a hook provides the confidence that there won't be unexpected changes, and this can be done quite trivially with &lt;a href="https://github.com/testing-library/react-hooks-testing-library"&gt;React Hook Testing Library&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With this testing library, one doesn't need to render out any UI to test functionality and can directly interact with the hook.&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;// useCounter.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useCallback&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;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;useCounter&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;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setCount&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&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;increment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setCount&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;x&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;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="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;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;increment&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// useCounter.test.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;renderHook&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;act&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;@testing-library/react-hooks&lt;/span&gt;&lt;span class="dl"&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;useCounter&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;./useCounter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should increment counter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;renderHook&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;useCounter&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

  &lt;span class="nf"&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;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the returned result, you'll be able to access a few things, such as previous and current results, as well as call methods directly without worrying about the following error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Invariant Violation: Hooks can only be called inside the body of a function component.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;I hope you've learned a thing or two about React Hooks and how to get started with building out our own. Your imagination is the limit!&lt;/p&gt;

&lt;p&gt;Leave a comment 💬 if you'd like to learn about any other topics related to React, Typescript/Javascript, Ruby, and GraphQL!&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>webdev</category>
      <category>react</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Production Ready React</title>
      <dc:creator>Joel Warrington</dc:creator>
      <pubDate>Tue, 18 Jan 2022 15:54:13 +0000</pubDate>
      <link>https://forem.com/jobber/production-ready-react-14hg</link>
      <guid>https://forem.com/jobber/production-ready-react-14hg</guid>
      <description>&lt;p&gt;&lt;strong&gt;A pragmatic guide to maintainable React components powered by GraphQL.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Writing performant, testable, and readable React can be thought of as an art, but instead, I hope to convince you that it can instead be achieved with a simple toolkit 🔧 , a few recipes 🧾 , and some examples 🧪 .&lt;/p&gt;

&lt;h2&gt;
  
  
  Starting with GraphQL
&lt;/h2&gt;

&lt;p&gt;One common approach and pitfall is to start implementation from the front-end and work backward towards your GraphQL API. In my experience, this can lead towards less-than-ideal schema design, negatively impacting the GraphQL experience for any other consumer of your API, where types end up as sacks of data void of any meaning and without purpose.&lt;/p&gt;

&lt;p&gt;Things to consider include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How will other frontend views get this data?&lt;/li&gt;
&lt;li&gt;How will nullability be applied?&lt;/li&gt;
&lt;li&gt;Will the data make sense to other developers?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Starting with GraphQL will not only be better for your schema, but it will also be better and crucial for you and your team when building maintainable front-end systems. To start building your GraphQL API and building a truly resilient schema, you should almost certainly consult &lt;a href="https://book.productionreadygraphql.com"&gt;Production Ready GraphQL&lt;/a&gt;, and a worthy companion tutorial; &lt;a href="https://github.com/Shopify/graphql-design-tutorial/blob/master/TUTORIAL.md"&gt;Designing a GraphQL API&lt;/a&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  Making the jump
&lt;/h2&gt;

&lt;p&gt;Now that we've got a production-ready GraphQL schema, how do we implement its consuming counterpart? Let's start with the most fundamental components in our larger application and explore some of the first tools in your toolbox 🔧 ; component composition and GraphQL fragments.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Fragments let you construct sets of fields, and then include them in queries where you need to.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Fragments can be used to achieve one concept commonly used in React; collocation, where you're able to contain all of the data needed from a component alongside your logic, styling, and rendering.&lt;/p&gt;

&lt;p&gt;In practice, you're able to create a component that doesn't need to query its data but instead provides an interface to how it should receive its data when used in other components (hence, component composition).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example 🧪&lt;/strong&gt;&lt;br&gt;
&lt;em&gt;A small component that receives all of its data from props and defines how it expects to receive data using a GraphQL fragment.&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h2&gt;
  
  
  Is this thing on?
&lt;/h2&gt;

&lt;p&gt;To build maintainable and shippable React, you'll want to test functionality in isolation in a way that makes it very clear when things break so that you and your team will be confident with every release.&lt;/p&gt;

&lt;p&gt;Testing React components isn't as hard as it's made to be, especially with modern-day tools such as &lt;a href="https://testing-library.com/docs/react-testing-library/intro/"&gt;React Testing Library&lt;/a&gt;, and &lt;a href="https://jestjs.io/"&gt;Jest&lt;/a&gt;, which take out all the guesswork of rendering and interacting with a component. Jest will act as the test runner and provide the overall structure of your unit tests, while React Testing Library will provide the methods needed to accurately assert functionality.&lt;/p&gt;

&lt;h3&gt;
  
  
  User functionality
&lt;/h3&gt;

&lt;p&gt;Getting started with &lt;a href="https://testing-library.com/docs/react-testing-library/intro/"&gt;React Testing Library&lt;/a&gt; is quick and no different than writing unit tests for functions and classes. Every test case should start by rendering a component with the render method and destructuring the return to get access to the rendered result, and document queries such as getByText, getByLabel (see the &lt;a href="https://testing-library.com/docs/react-testing-library/cheatsheet/#queries"&gt;query cheat sheet&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example 🧪&lt;/strong&gt;&lt;br&gt;
&lt;em&gt;An example test file using React Testing Library and our previously defined &lt;code&gt;Message&lt;/code&gt; component. Showing how one could test user functionality with predictable results.&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h3&gt;
  
  
  Cloudy with a chance of bugs ⛅🐜🐛
&lt;/h3&gt;

&lt;p&gt;Jest uses a package called &lt;a href="https://istanbul.js.org/"&gt;Istanbul&lt;/a&gt; to provide test coverage metrics such as statement, branch, function, and line coverage so that you can understand and enforce the quality of your test suite, providing more confidence in releases.&lt;/p&gt;

&lt;p&gt;It's highly recommended you set coverage thresholds that make you comfortable and don't burden you with covering every test case and flow, and subsequently integrate coverage tests into your CI/CD pipeline and block failing builds.&lt;/p&gt;

&lt;p&gt;To get started and collect test coverage, you can run the following command (or add the --coverage argument to your existing node test script):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx jest &lt;span class="nt"&gt;--coverage&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Some things to keep in mind when trying to achieve high coverage:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;test units should interact with your component using every variation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;if&lt;/strong&gt; and &lt;strong&gt;early return&lt;/strong&gt; statements need to be considered and both or multiple paths need to be tested&lt;/li&gt;
&lt;li&gt;optional chaining (&lt;strong&gt;?.&lt;/strong&gt;) gets increasingly harder to test the longer the chain and GraphQL type nullability should be a factor to consider when building your component&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At Jobber, our test coverage targets sit around 85%!&lt;/p&gt;
&lt;h2&gt;
  
  
  Putting the pieces together 🧩
&lt;/h2&gt;

&lt;p&gt;Now that we've defined our component and how it will get its data, how can we use it at scale and alongside other components? Let's take a look at the loader concept and how to bubble up your fragment!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example 🧪&lt;/strong&gt;&lt;br&gt;
&lt;em&gt;Multiple components showing how one can reuse and build larger components made up of smaller components that implement fragment collocation and data fetching (using a loader component). Using the previously defined &lt;code&gt;Message&lt;/code&gt; component.&lt;/em&gt;&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;




&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Using the &lt;code&gt;loader&lt;/code&gt; pattern, we can easily implement and test our &lt;code&gt;Conversation&lt;/code&gt; component and handle query transformation in our loader component. In addition, we don't need to mock as much using &lt;code&gt;MockedProvider&lt;/code&gt; from Apollo (&lt;a href="https://www.apollographql.com/docs/react/development-testing/testing/"&gt;see testing react components using Apollo&lt;/a&gt;). In addition, queries or fragments will be much easier to add when using a tool such as &lt;a href="https://www.graphql-code-generator.com/"&gt;GraphQL Code Generator&lt;/a&gt; our Typescript types become very easy to implement!&lt;/p&gt;

&lt;h2&gt;
  
  
  About Jobber
&lt;/h2&gt;

&lt;p&gt;Interested in React &amp;amp; GraphQL? Consider joining Jobber or contributing to our open-source design system &lt;a href="https://atlantis.getjobber.com/"&gt;Atlantis&lt;/a&gt;. We're hiring for remote positions across Canada at all software engineering levels! &lt;/p&gt;

&lt;p&gt;Our awesome Jobber technology teams span across Payments, Infrastructure, AI/ML, Business Workflows &amp;amp; Communications. We work on cutting edge &amp;amp; modern tech stacks using React, React Native, Ruby on Rails, &amp;amp; GraphQL. &lt;/p&gt;

&lt;p&gt;If you want to be a part of a collaborative work culture, help small home service businesses scale and create a positive impact on our communities, then visit our careers site to learn more!help small home service businesses scale and create a positive impact on our communities, then visit our &lt;a href="https://getjobber.com/about/careers?utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=eng_blog"&gt;careers&lt;/a&gt; site to learn more!&lt;/p&gt;

</description>
      <category>react</category>
      <category>graphql</category>
      <category>webdev</category>
      <category>typescript</category>
    </item>
  </channel>
</rss>
