<?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: Sam Markham</title>
    <description>The latest articles on Forem by Sam Markham (@sharkham).</description>
    <link>https://forem.com/sharkham</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%2F407825%2Fd452be71-ae65-404a-8bc4-9a3cdbfbce97.jpeg</url>
      <title>Forem: Sam Markham</title>
      <link>https://forem.com/sharkham</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/sharkham"/>
    <language>en</language>
    <item>
      <title>Project planning for a React/Rails Writing Challenge app</title>
      <dc:creator>Sam Markham</dc:creator>
      <pubDate>Thu, 25 Feb 2021 21:17:29 +0000</pubDate>
      <link>https://forem.com/sharkham/project-planning-for-a-react-rails-writing-challenge-app-4014</link>
      <guid>https://forem.com/sharkham/project-planning-for-a-react-rails-writing-challenge-app-4014</guid>
      <description>&lt;p&gt;Last April I built an app for a novel contest run by an online writers group. It was set up so users could sign up for the app, make an entry for their novel, and then use the app to track their writing progress, as well as see the writing progress of other users. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2pac54q2aslebr3w7iio.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2pac54q2aslebr3w7iio.png" alt="A novel tracker app featuring several novel cards with information on different projects"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The concept was basically to make something similar to the Nanowrimo website (National Novel Writing Month for those of you who don’t hang out in writing spaces as often as I do!), but smaller and focused on the specific functionality the organizers of the contest wanted.&lt;/p&gt;

&lt;p&gt;I launched it, the group used it, and it was great!&lt;/p&gt;

&lt;p&gt;And then a few months later, I got a request for another version of the site for a different group to use. I spun up and hosted a new version—changing a few things like the yearly rollover of novel projects—but doing that got me thinking. Creating this site for another group required another whole instance of the site, and if I ever wanted to do this again for another group, this really wasn't efficient. If I took some time to rewrite the app's code, I could instead give users the ability to run and manage multiple contests within the same app. &lt;/p&gt;

&lt;p&gt;With that in mind, I set out to make a version of the app that would support users creating and running their own contests, multiple if they wanted. &lt;/p&gt;

&lt;p&gt;And that starts with project planning, which is what I’ve been doing this week!&lt;/p&gt;

&lt;h2&gt;
  
  
  The Notes
&lt;/h2&gt;

&lt;p&gt;To start off with, I walked through some user stories—what I wanted users to be able to do with the app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- One user story:
  - A user signs up for the app
  - They make their own contest
  - As the contest admin, they can set a date for when the contest ends, if they want it to end
  - They have/generate a link to let other people join the contest, and send that out
  - They can make other users in their contest admins too, if they want 
  - They make a novel, associate it with their own contest
  - They view their novel, and also edit it
  - With the same account, they can create, view, and/or sign up for other contests if they like as well
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- Another user story:
  - A user receives a link for a friend to sign up for this app
  - They sign up for the app first
  - They enter the token to sign up for that specific contest
  - This prompts them to create a novel, and they have the option to add the novel to that contest, since they have joined the contest
 - The user creates another novel that they just want to have for themselves
    - This novel is not associated with any contest and won't show up on any contest pages
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Models
&lt;/h2&gt;

&lt;p&gt;From the user stories I had come up with, I knew that I needed some complex relationships with my database models here. Users needed to be able to have many contests, and contests should also have many users. I also wanted users to be able to have different relationships to their contests—one user may be an admin of two contests, but just a member of a third, for example. &lt;/p&gt;

&lt;p&gt;At this point, this started to sound familiar, and I realized I had come up with some very similar models for a previous project of mine, a &lt;a href="https://dev.to/sharkham/creating-two-resources-at-once-in-rails-386c"&gt;collaborative novel writing app built in Rails&lt;/a&gt;. In that app, meant for online collaboration on novel projects, users had many novels through memberships, which also included a role attribute that would designate the user an admin or a regular member of the novel project. &lt;/p&gt;

&lt;p&gt;Adjusting this a little, from novels to contests, I was in business! I also decided to change “contests” to “challenges.” Despite the original use for it being to run a novel contest, “writing challenges” is a lot more generalizable and represents more use cases for the app, so it made sense to start from here on the model level. Contests became “challenges”, and novels became “projects.”&lt;/p&gt;

&lt;p&gt;I also wanted projects to be able to be a part of multiple challenges, but that was a simpler join table. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvobcciyeye2j7elgrepm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvobcciyeye2j7elgrepm.png" alt="A schema diagram with seven tables: users, memberships, challenges, projects, challenges_projects, badges, and badgetypes."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I used drawSQL to diagram this because it was free, visually appealing and easy to use, allowing me to drag tables around to organize things in a way that made it easier to interpret visually. &lt;/p&gt;

&lt;h2&gt;
  
  
  The Sketches
&lt;/h2&gt;

&lt;p&gt;I did these sketches at the same time as I was diagramming my models. The sketches helped me make sure I had everything I wanted in my models, and vice versa. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F36sy6t08y17voau8lh0y.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F36sy6t08y17voau8lh0y.jpg" alt="A pencil sketch of the main page of a writing challenge app, featuring two open dropdown menus for challenges and projects as well as some project cards with titles and word count progress bars on them."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The main difference I knew that I wanted on the frontend from my original app was I wanted users to be able to easily view the splash pages for multiple challenges, and also create new challenges. In addition to that, I wanted them to be able to easily view all of their projects. The layout will probably change when I get to the React part of this project, but for now, dropdown menus made sense. The project cards are taken directly from how they’re laid out in my novel contest app. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8kphllc8vqk5vaur1wc8.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8kphllc8vqk5vaur1wc8.jpg" alt="A pencil sketch of a challenge page on the writing challenge app, featuring more novel cards and tabs for projects and guidelines."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With multiple challenges, it didn’t make sense to have the challenge guidelines in the main nav bar anymore, so here I have the projects and guidelines views as two tabs on the main challenge page. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdjatydygs0y41wxsdcsf.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdjatydygs0y41wxsdcsf.jpg" alt="A pencil sketch of a user page on the writing challenge app. It includes a username and bio section, as well as several project cards similar to those in previous sketches."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Another thing I wanted to add to this app, now that I have figured out it’s possible to &lt;a href="https://dev.to/sharkham/changing-attributes-on-a-user-in-rails-without-having-to-re-enter-password-5bgb"&gt;update a user model without having to update the password&lt;/a&gt; was user pages with a bio and the user’s projects. Ideally, the user should be able to set them to private vs public so all of a user’s projects would show up for them but only “public” projects would show up for other users. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn3wogh8s8kofjdzdgvlo.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn3wogh8s8kofjdzdgvlo.jpg" alt="A pencil sketch of a contest settings page on the writing challenge app, featuring a list of editable features such as name, guidelines, start date, end date, and member roles."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, a very draft-y admin panel for challenges. I wanted users to be able to change a variety of settings, such as being able to set challenges as open/closed, public/private, and also to determine if they should have set start and end dates. This is one of the sections that is likely to change as I go along, just because there's so much functionality I could add here. Keeping minimum viable product in mind though, this is what I'm going with for now.&lt;/p&gt;

&lt;h2&gt;
  
  
  What comes next?
&lt;/h2&gt;

&lt;p&gt;Now, with a better idea of my user stories and my models planned out, I’m ready to start building the Rails API backend for this project! More on that soon.&lt;/p&gt;

&lt;p&gt;This project is in progress, so if you have any questions, suggestions, or feedback you’d like to leave on the project, please feel free!&lt;/p&gt;

</description>
      <category>codenewbie</category>
      <category>rails</category>
      <category>react</category>
    </item>
    <item>
      <title>Getting started in Preact (by rebuilding a React app)</title>
      <dc:creator>Sam Markham</dc:creator>
      <pubDate>Mon, 30 Nov 2020 20:46:32 +0000</pubDate>
      <link>https://forem.com/sharkham/getting-started-in-preact-by-rebuilding-a-react-app-370k</link>
      <guid>https://forem.com/sharkham/getting-started-in-preact-by-rebuilding-a-react-app-370k</guid>
      <description>&lt;p&gt;A few weeks ago, after reading through the Preact docs, I decided to make a version of my React writing prompt generator in Preact to get more familiar with the library.&lt;/p&gt;

&lt;p&gt;For those who haven’t heard of it (like I hadn’t!), Preact is a small, lightweight library with a lot of similar functionality to React, other than a few key differences. Preact more closely follows DOM specifications (e.g. it allows the use of &lt;code&gt;class&lt;/code&gt; instead of &lt;code&gt;className&lt;/code&gt;, though both are supported), and it uses the browser’s native event system instead of a synthetic one like React does, to name a couple. &lt;/p&gt;

&lt;p&gt;The Preact documentation states that it’s possible to move React apps over to Preact via &lt;a href="https://preactjs.com/guide/v10/switching-to-preact#setting-up-compat"&gt;aliasing&lt;/a&gt; &lt;code&gt;react&lt;/code&gt; and &lt;code&gt;react-dom&lt;/code&gt; to &lt;code&gt;preact/compat&lt;/code&gt; (short for compatibility), and it has some information on &lt;a href="https://preactjs.com/guide/v10/getting-started#aliasing-react-to-preact"&gt;how this is configured in various bundlers&lt;/a&gt;, but because I wanted to get more familiar with using Preact directly, I decided to start with Preact from the get-go and just move some of my code over manually instead. &lt;/p&gt;

&lt;p&gt;This is what I did! &lt;/p&gt;

&lt;h2&gt;
  
  
  1. Start with the Preact CLI
&lt;/h2&gt;

&lt;p&gt;Following along with the &lt;a href="https://preactjs.com/guide/v10/getting-started#best-practices-powered-by-preact-cli"&gt;docs&lt;/a&gt;, I started by installing the Preact CLI on my computer, and then using it to create a new project:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;preact create default preact-writing-prompt-generator
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;default&lt;/code&gt; flag here refers to the default template for the Preact CLI, available on GitHub &lt;a href="https://github.com/preactjs-templates/default"&gt;here&lt;/a&gt;. I ended up having some minor issues with this set up later, but it was a good place to start because it gave me some code right out of the box that I could tinker with instead of building from scratch. &lt;/p&gt;

&lt;p&gt;Following along with the tutorial, I spun up a development server, and then started poking around the files.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd preact-writing-prompt-generator

npm run dev

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

&lt;/div&gt;



&lt;p&gt;From here, it was time to rebuild my components. &lt;/p&gt;

&lt;h2&gt;
  
  
  2. JSX in Preact, and alternatives
&lt;/h2&gt;

&lt;p&gt;There are differences between React and Preact components, but not as many as I thought there would be going into this endeavour. Part of this is, of course, because while it doesn’t include every React feature (by design!), Preact was designed to achieve 100% compatibility with React with the &lt;code&gt;preact/compat&lt;/code&gt; layer. &lt;/p&gt;

&lt;p&gt;Without using &lt;code&gt;preact/compat&lt;/code&gt; though, I still had a few options for how to set up my Preact components. &lt;/p&gt;

&lt;p&gt;By default, React works with JSX, a syntactic extension to JavaScript that allows you to code what basically looks like HTML directly in JavaScript. Using JSX requires using a build step: the JSX code needs to be converted into JavaScript functions, which then make up the virtual DOM (&lt;a href="https://medium.com/javascript-in-plain-english/what-the-heck-is-the-virtual-dom-3ef1ae4a950b"&gt;used to update the real DOM&lt;/a&gt;). &lt;/p&gt;

&lt;p&gt;Preact can be set up without using JSX, beneficial if you’d like to avoid a build step. &lt;/p&gt;

&lt;p&gt;This is an example of setting up Preact to work directly in the browser, without JSX, from the Preact &lt;a href="https://preactjs.com/guide/v10/getting-started"&gt;docs&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;module&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;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;h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;render&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;https://unpkg.com/preact?module&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Create your app&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;h1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello World!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&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;/script&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Preact components can also be built with Hyperscript/HTM, which is a syntax that looks similar to JSX but doesn’t require a build step (see another example from the Preact docs):&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;module&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;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;h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;render&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;https://unpkg.com/preact?module&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;htm&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;https://unpkg.com/htm?module&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Initialize htm with Preact&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;htm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="s2"&gt;`&amp;lt;h1&amp;gt;Hello &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;!&amp;lt;/h1&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="s2"&gt;`&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; name="World" /&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&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;/script&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, as I was already comfortable working in JSX from my experience with React, and as I wasn’t bothered by having a build step in this project, I decided to go with that. &lt;/p&gt;

&lt;p&gt;Without using &lt;code&gt;preact/compat&lt;/code&gt; though, JSX still looks a little different in Preact. &lt;/p&gt;

&lt;p&gt;Basically, to use some more example code from the Preact docs, JSX that looks like this:&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;span&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Home&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/span&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;/a&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;would look like this in React when converted to function calls to build the virtual DOM:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;a&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;span&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Home&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and like this in Preact (without using &lt;code&gt;preact/compat&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;a&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;span&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Home&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As far as I can tell, Preact uses a custom pragma (&lt;a href="https://johno.com/jsx-pragma/"&gt;a “directive” that tells a compiler how to handle the input&lt;/a&gt;), named &lt;code&gt;h&lt;/code&gt; by the creator of Preact because the original idea for a JSX-like builder function was called &lt;a href="https://github.com/dominictarr/hyperscript"&gt;hyperscript&lt;/a&gt;, to do pretty much the same thing React’s &lt;code&gt;React.createElement()&lt;/code&gt; is doing. Because this function isn’t namespaced to Preact though, it has to be imported separately. &lt;/p&gt;

&lt;p&gt;What both of these ways of writing JSX have in common, is that they tell the transpiler (in this case it's Babel, which both React and this implementation of Preact use) that this &lt;code&gt;h&lt;/code&gt; function should be called at runtime for each node.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Components
&lt;/h2&gt;

&lt;p&gt;With the way I was going to write my components figured out, I could start moving them over from my React project and editing them into Preact syntax!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;h&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;preact&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Router&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;preact-router&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Header&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;./header&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Home&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;../routes/home&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Nautical&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;../routes/nautical&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;About&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;../routes/about&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;app&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Header&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Router&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Home&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Nautical&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/nautical&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;About&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/about&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Router&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;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;This component imports &lt;code&gt;h&lt;/code&gt; from Preact instead of &lt;code&gt;React&lt;/code&gt; from React, but there aren’t any differences as to how that affects this component.&lt;/p&gt;

&lt;p&gt;It also uses &lt;code&gt;preact-router&lt;/code&gt; instead of &lt;code&gt;react-router-dom&lt;/code&gt;, which does feature some different syntax. For example, in React, the routes in the code snippet above would be written like this:&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;// note that in React, Router is called Browser Router, but it is convention to import BrowserRouter as Router&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Router&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Route&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="err"&gt;“&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="err"&gt;“&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Home&lt;/span&gt;&lt;span class="o"&gt;/&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Route&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;Route&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="err"&gt;“&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="err"&gt;“&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Nautical&lt;/span&gt;&lt;span class="o"&gt;/&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Route&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;Route&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="err"&gt;“&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="err"&gt;“&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;About&lt;/span&gt;&lt;span class="o"&gt;/&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Route&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;/Router&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Other than that, Preact components can pretty much look like React components! &lt;/p&gt;

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

&lt;p&gt;Overall I really enjoyed the experience of translating the app I’d built into the Preact library! The main difficulty I ran into was figuring out how JSX worked in Preact, and that was mostly just to write this post—the Preact templates were intuitive to use even without that information. (Though on that note, thank you to Nick Taylor for sending me &lt;a href="https://jasonformat.com/wtf-is-jsx/"&gt;this article&lt;/a&gt;, which helped a lot!)&lt;/p&gt;

&lt;p&gt;The other stumbling block for me was that the file structure in the Preact CLI was quite different from what I’m used to setting up for React projects. As far as I can tell that was specific to that template though, not related to the library itself, and I was still able to figure it out quite quickly even if it isn’t how I would usually set up my own project. &lt;/p&gt;

&lt;p&gt;I hope this post is useful to anyone else thinking of trying out Preact, and if you have questions, comments, or suggested corrections, please let me know in the comments! &lt;/p&gt;

</description>
      <category>react</category>
      <category>preact</category>
      <category>tutorial</category>
      <category>codenewbie</category>
    </item>
    <item>
      <title>Building a writing prompt generator with functional React</title>
      <dc:creator>Sam Markham</dc:creator>
      <pubDate>Wed, 09 Sep 2020 02:13:42 +0000</pubDate>
      <link>https://forem.com/sharkham/building-a-writing-prompt-generator-with-functional-react-1k1o</link>
      <guid>https://forem.com/sharkham/building-a-writing-prompt-generator-with-functional-react-1k1o</guid>
      <description>&lt;p&gt;As well as a dev, I’m a fiction writer, and sometimes I run out of ideas for what to write. A couple of weeks ago this gave me an idea for what to code—a &lt;a href="https://promptgenerator.netlify.app/" rel="noopener noreferrer"&gt;writing prompt generator&lt;/a&gt;! &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F8ww354qlpdypw5ohs7s2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F8ww354qlpdypw5ohs7s2.png" alt="Screenshot of prompt generator app"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are a lot of these out there, of course, but making one myself was fun, and as my previous React app was made with mostly class components, for this project I took the opportunity to get more familiar with function components in React along the way.&lt;/p&gt;

&lt;p&gt;This post will walk through how I set this up! It assumes some familiarity with React as a library.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Multiple generator pages
&lt;/h2&gt;

&lt;p&gt;The first thing I wanted to accomplish with this generator was allowing the user to generate prompts from multiple sets of words, with different pages for each set of prompts. To this aim, I created two prompt page components and one generator component that could be rendered with a different set of props in each of them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Generator&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;./Generator&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;words&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;../assets/words&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;WordsPromptPage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&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;Write&lt;/span&gt; &lt;span class="mi"&gt;250&lt;/span&gt; &lt;span class="nx"&gt;words&lt;/span&gt; &lt;span class="na"&gt;about&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="nx"&gt;Generator&lt;/span&gt; &lt;span class="nx"&gt;dataset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;words&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;/div&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;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;WordsPromptPage&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Here the WordsPromptPage component is rendering a Generator component with a dataset of &lt;code&gt;words&lt;/code&gt;, which is an array in a separate file. I could have made a database for different types of prompts, but I decided to keep them in asset files on the frontend instead to be able to host the generator more simply as a frontend on Netlify instead of hosting a backend and frontend separately. &lt;/p&gt;

&lt;h2&gt;
  
  
  2. Navigation
&lt;/h2&gt;

&lt;p&gt;To switch between different generator pages (and my about page) in the app, I used &lt;code&gt;react-router-dom&lt;/code&gt; in the main &lt;code&gt;App&lt;/code&gt; component so that when the URL changes, the component shown changes too.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./App.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;WordsPromptPage&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;./components/WordsPromptPage&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;NauticalWordsPromptPage&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;./components/NauticalWordsPromptPage&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;Switch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Route&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-router-dom&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;NavBar&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;./components/NavBar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;AboutPage&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;./components/AboutPage&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;App&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;App&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;NavBar&lt;/span&gt;&lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Prompt&lt;/span&gt; &lt;span class="nx"&gt;Generator&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&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;Switch&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Route&lt;/span&gt; &lt;span class="nx"&gt;exact&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/nauticalwords&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;NauticalWordsPromptPage&lt;/span&gt;&lt;span class="o"&gt;/&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Route&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;Route&lt;/span&gt; &lt;span class="nx"&gt;exact&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/about&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;AboutPage&lt;/span&gt;&lt;span class="o"&gt;/&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Route&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;Route&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;WordsPromptPage&lt;/span&gt;&lt;span class="o"&gt;/&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Route&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;/Switch&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;/div&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;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I used &lt;code&gt;Switch&lt;/code&gt; here because I want only one matched &lt;code&gt;Route&lt;/code&gt; to render and &lt;code&gt;Switch&lt;/code&gt; renders a route exclusively—I don’t want users to be able to type in &lt;code&gt;/nauticalwords&lt;/code&gt; and have &lt;code&gt;WordsPromptPage&lt;/code&gt; and &lt;code&gt;NauticalWordsPromptPage&lt;/code&gt; both render on the same page because &lt;code&gt;/nauticalwords&lt;/code&gt; matches both &lt;code&gt;/nauticalwords&lt;/code&gt; and &lt;code&gt;/&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;Switch&lt;/code&gt; is below the heading and the &lt;code&gt;NavBar&lt;/code&gt; component so that while part of the page will change according to the URL, the heading and navigation will always render. &lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;NavBar&lt;/code&gt; component, I put the links to these different routes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Link&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-router-dom&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;NavBar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;nav&lt;/span&gt; &lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;navigation&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Link&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;nav-link&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Words&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Link&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;Link&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;nav-link&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/nauticalwords&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Nautical&lt;/span&gt; &lt;span class="nx"&gt;Words&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Link&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;Link&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;nav-link&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/about&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;About&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Link&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;/nav&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;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;NavBar&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Function components
&lt;/h2&gt;

&lt;p&gt;The last app I built with React I used mostly class components—the function components I did use were simple presentational components: components mostly concerned with how things look, often (and in my case) written as stateless function components. When I needed to deal with state, I stuck with class components. &lt;/p&gt;

&lt;p&gt;For this project, I wanted to start getting more familiar with function components, specifically using hooks to be able to deal with state in function components. &lt;/p&gt;

&lt;p&gt;According to the React documentation, hooks are “functions that let you 'hook into' React state and lifecycle features from function components… they let you use React without classes.”&lt;/p&gt;

&lt;p&gt;For my generator component, I used useState to return a &lt;code&gt;prompt&lt;/code&gt; variable and also a &lt;code&gt;setPrompt&lt;/code&gt; function to let me update the prompt.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&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="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Generator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="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;prompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setPrompt&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="err"&gt;“&lt;/span&gt;&lt;span class="nx"&gt;Click&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;generate&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;prompt&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="err"&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;generate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)];&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;


  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&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="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;prompt&lt;/span&gt;&lt;span class="dl"&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;prompt&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="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;setPrompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;generate&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;Generate&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="sr"&gt;/div&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;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;Generator&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;The initial state here was set to the string, “Click to generate a prompt!”, and on button click, &lt;code&gt;prompt&lt;/code&gt; is set to the return value of the &lt;code&gt;generate()&lt;/code&gt; function. &lt;/p&gt;

&lt;p&gt;After getting this set up though, I realized it would be a better idea to have the site load with a random prompt generated as well, and I could probably use &lt;code&gt;useState&lt;/code&gt; to do this too.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&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="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Generator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;generate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)];&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setPrompt&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;generate&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;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&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="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;prompt&lt;/span&gt;&lt;span class="dl"&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;prompt&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="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;setPrompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;generate&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;Generate&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="sr"&gt;/div&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;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;Generator&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;As long as the &lt;code&gt;generate()&lt;/code&gt; method was declared before &lt;code&gt;useState&lt;/code&gt; was set up (because JavaScript &lt;a href="https://blog.bitsrc.io/hoisting-in-modern-javascript-let-const-and-var-b290405adfda" rel="noopener noreferrer"&gt;doesn’t hoist function expressions&lt;/a&gt;), I could set the initial state for &lt;code&gt;prompt&lt;/code&gt; to the return value of &lt;code&gt;generate()&lt;/code&gt; as well, so that the app would have a newly generate prompt on loading and on refresh. &lt;/p&gt;

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

&lt;p&gt;I had a lot of fun building this app, and it was a good way to spend a few days familiarizing myself with function components, as well as spending more time with React after a break away from it! I look forward to diving deeper into functional React in the future—and expanding the types of writing prompts in the generator! &lt;/p&gt;

</description>
      <category>react</category>
      <category>codenewbie</category>
    </item>
    <item>
      <title>6 things I learned from building my first app for a client</title>
      <dc:creator>Sam Markham</dc:creator>
      <pubDate>Mon, 17 Aug 2020 22:52:50 +0000</pubDate>
      <link>https://forem.com/sharkham/6-things-i-learned-from-building-my-first-app-for-a-client-4dbj</link>
      <guid>https://forem.com/sharkham/6-things-i-learned-from-building-my-first-app-for-a-client-4dbj</guid>
      <description>&lt;p&gt;Recently I built my first app for a client.&lt;/p&gt;

&lt;p&gt;A friend of mine who runs a novel-writing contest had mentioned earlier this year that she really liked my last app, and that she wanted to do something similar for the contest at some point, so when I was looking at building a React app in April, I offered to build one for her. &lt;/p&gt;

&lt;p&gt;It was a wonderful opportunity to code something fun and writing-related (I am a novelist in my other life), and the fact that it was in use right after I finished was a bonus!&lt;/p&gt;

&lt;p&gt;This is what I learned in the process.  &lt;/p&gt;

&lt;h2&gt;
  
  
  1. Set expectations
&lt;/h2&gt;

&lt;p&gt;The first thing I did, even before offering to build the app, was had a conversation with my potential client about what she was looking for and whether that aligned with what I felt I would be able to make with my current skill level—and in a reasonable amount of time. I wanted to make sure I wasn’t committing to do something that either I wouldn’t be able to do, or that wouldn’t meet her requirements. &lt;/p&gt;

&lt;p&gt;After this initial conversation, I also wrote out a list of specific website features in plain terms that corresponded to how I would have to set up the code, and then asked my client to confirm, correct, or add to this list. This helped give her an idea of what types of details I needed to know, and when she added to the list, gave me an idea of what exactly I needed to build! &lt;/p&gt;

&lt;p&gt;The initial list:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- Secure user sign-up/login 
- User has many novels (to make reusable for future years if necessary!) 
- Novels include info like: title, summary, word count 
- Badges that can be dragged over (or turned on or something)  
- Word count tracker 
- Should there be a section for users to see some things about each other, like number of folks participating, etc? 
- Some sort of calendar function for days past/days left? (Not sure about setting this one up) 
- About page/drop down with a bit of info on what this is/how this works  
- Admin access for you to change some things  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Making sure my client and I were on the same page about what I would and wouldn’t be doing with this app was essential to keeping things moving smoothly and efficiently down the road. &lt;/p&gt;

&lt;h2&gt;
  
  
  2. MVP
&lt;/h2&gt;

&lt;p&gt;Speaking of expectations, a minimum viable product is also a useful thing to keep in mind, especially for building something for someone else.&lt;/p&gt;

&lt;p&gt;Before I began work on the app we also discussed what features were necessary to launch the app and what features could be added on later, or if I had time. This meant I had a clear checklist of features to work from while keeping in mind other things that could be useful for this project in the future. &lt;/p&gt;

&lt;h2&gt;
  
  
  3. Cultivate the ability to explain your code to a non-coding audience
&lt;/h2&gt;

&lt;p&gt;Early in the process of building my novel progress tracking app, one of the features my client asked for was a word count tracker that let users edit their word count for a specific day and involved a graph to display writing progress over time—something like what the Nanowrimo (National Novel Writing Month) website, which inspired some of my functionality, used. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F4d50f44tpgr3hn8h7f8u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F4d50f44tpgr3hn8h7f8u.png" alt="Nanowrimo Wordcount Breakdown"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fa9wjk5axv5dz5rnh8r2z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fa9wjk5axv5dz5rnh8r2z.png" alt="Nanowrimo Wordcount Graph"&gt;&lt;/a&gt;&lt;br&gt;
(Screenshots from my Nanowrimo progress in 2017)&lt;/p&gt;

&lt;p&gt;This was something that seemed simple from a non-coding perspective but actually would have significantly increased the complexity of this part of the project. &lt;/p&gt;

&lt;p&gt;In the database, I had initially set the &lt;code&gt;novel&lt;/code&gt; model to have one &lt;code&gt;wordcount&lt;/code&gt; attribute, which users can update on the frontend. Nanowrimo, a 30-day contest, has 30 different &lt;code&gt;wordcount&lt;/code&gt;-type attributes, one for each day, and because the contest I was designing the app for ran all year, this would involve creating 365 different &lt;code&gt;wordcount&lt;/code&gt; attributes for each novel. &lt;/p&gt;

&lt;p&gt;In addition to increasing the amount of data each novel would have to hold, a year’s worth of &lt;code&gt;wordcount&lt;/code&gt; attributes would mean finding a visually appealing way for the user to edit each of them—a simple list of that many days wouldn’t cut it. &lt;/p&gt;

&lt;p&gt;Though this was completely doable, it would take more time, and the client was eager to have the website up and running.&lt;/p&gt;

&lt;p&gt;When altering the word count tracker came up as a requested feature, my ability to explain how much coding work this would be in plain terms—that it would be more like adding an extra feature than making a slight change to an existing feature—meant that my client understood why this was something that couldn’t be added to the first version of the app while keeping the short turnaround time. As a result, she was happy to include this feature on the list of stretch goals instead. &lt;/p&gt;

&lt;h2&gt;
  
  
  4. Mock-ups help
&lt;/h2&gt;

&lt;p&gt;Even though I only did a quick sketch of what I was thinking the web app would look like, I found this was a great way to get on the same page with my client about what I was building for her. It was also a handy thing to work from when I was building functionality!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fkcqeajqng743apounb3g.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fkcqeajqng743apounb3g.jpg" alt="Sketch of website"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Working to a deadline is challenging and rewarding
&lt;/h2&gt;

&lt;p&gt;This is one I already knew on some level—my coding projects previous to this one had all been completed in two weeks, these deadlines set by my bootcamp, but while these projects had to pass a certain amount of requirements in that time, they didn’t have to be completely finished and deployed by then. &lt;/p&gt;

&lt;p&gt;For this app, I agreed with my client on a deadline of four weeks to have the app deployed and ready for users. &lt;br&gt;
 &lt;br&gt;
Also, I decided that in addition to building a finished React app with the functionality we had agreed on, I needed to add forgot/reset password functionality by this deadline to prepare for real users. This was something I had never done or even researched before—and so was deploying the app, both frontend and backend, for that matter. But I do love a challenge. I buckled down, did a lot of reading, watching videos, and fixing errors in my code, and on the evening of the deadline, my app was deployed and ready for users. &lt;/p&gt;

&lt;p&gt;Overall I found the experience very rewarding: having a reasonable deadline meant something to push myself toward, and the final result of being able to reach the goal set for me was a wonderful feeling. &lt;/p&gt;

&lt;p&gt;And speaking of: &lt;/p&gt;

&lt;h2&gt;
  
  
  6. Seeing your work in use is really satisfying!
&lt;/h2&gt;

&lt;p&gt;Deploying an app for immediate use by multiple users was a great opportunity to think about what was actually needed for user functionality in an app, and also to work with a specific use case that already existed. And the end result of seeing other folks using something I’ve built has reaffirmed why I’m a software engineer in the first place: to solve problems, and to build useful things. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fd0hbp3jioaml0c834c93.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fd0hbp3jioaml0c834c93.png" alt="Screencap of novel-tracker-app"&gt;&lt;/a&gt;&lt;br&gt;
(The version of this app in use by the contest is private; this is a screenshot of my demo deployed with the same code!)&lt;/p&gt;

&lt;p&gt;Going forward armed with this experience, I look forward to building many more apps for clients in the future! &lt;/p&gt;

</description>
      <category>codenewbie</category>
    </item>
    <item>
      <title>Vertical project planning for a Vanilla JavaScript/Rails app</title>
      <dc:creator>Sam Markham</dc:creator>
      <pubDate>Sun, 12 Jul 2020 04:59:53 +0000</pubDate>
      <link>https://forem.com/sharkham/vertical-project-planning-for-a-vanilla-javascript-rails-app-3k43</link>
      <guid>https://forem.com/sharkham/vertical-project-planning-for-a-vanilla-javascript-rails-app-3k43</guid>
      <description>&lt;p&gt;When I was building my &lt;a href="https://github.com/sharkham/PokeTeamBuilder"&gt;PokéTeamBuilder&lt;/a&gt; JavaScript/Rails API app, one suggestion I heard from my bootcamp was that to make things easier on myself, I should build my project out vertically instead of horizontally. It took me a while to really grasp what this meant, but when I did, it was very helpful to me for planning this project, and also as a project to-do list as I continued to add new features!&lt;/p&gt;

&lt;h2&gt;
  
  
  The Concept
&lt;/h2&gt;

&lt;p&gt;If you plan your projects horizontally, which in this case means: planning &lt;em&gt;all&lt;/em&gt; of your database models, then doing &lt;em&gt;all&lt;/em&gt; of your database migrations, then &lt;em&gt;all&lt;/em&gt; of your controller actions, then &lt;em&gt;all&lt;/em&gt; of your data fetching on the front-end, etc, you're likely to have to go back and redo things, because you're probably not going to be able to guess everything all of your features need to do up front!&lt;/p&gt;

&lt;p&gt;Vertical planning, in this case, means starting with one feature and taking it all the way from model to styling, and then moving on to the next thing. Working on my project, I wasn't able to apply this perfectly in practice, but that didn't matter--it still made a wonderful frame to work from, and made the work of setting up something with this many features a lot more manageable!&lt;/p&gt;

&lt;h2&gt;
  
  
  The Example
&lt;/h2&gt;

&lt;p&gt;The goal for my PokéTeam builder app was to have an app that would let a user select 6 Pokémon and a Trainer Sprite, move them around on the screen after they appeared, and then then take a picture of the team for posterity. &lt;/p&gt;

&lt;p&gt;There were a lot of moving parts here, but the first feature I started with was the Pokédex--I needed a drop-down menu with all of the available Pokémon that would be in the app for the user to select from them to make instances of individual Pokémon to drag around. &lt;/p&gt;

&lt;p&gt;After making a new Rails API with &lt;code&gt;rails new poketeam-builder-api --api&lt;/code&gt;, I: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Set up the model&lt;/li&gt;
&lt;li&gt;Wrote the seeds file to get the data I wanted from the existing &lt;a href="https://pokeapi.co/"&gt;PokéAPI&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Set up the controller actions&lt;/li&gt;
&lt;li&gt;Wrote a fetch request on the front end to get this data into the view&lt;/li&gt;
&lt;li&gt;Dealt with the view logic--in this case, getting all possible Pokémon from the Pokédex to show up in six dropdown menus. &lt;/li&gt;
&lt;li&gt;(I did not deal with the styling until after the project, but, using this method, I really could have dealt with it here if I had been feeling less pressed for time!)&lt;/li&gt;
&lt;li&gt;Moved on to the next feature: allowing users to create an individual Pokémon to move around the screen. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is what (part of) my to-do list looked like for this project--I highlighted boxes yellow once I had finished something. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ew9jUMQv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh3.googleusercontent.com/anWdkKav-audI4f6V0YyVG4mbzQaPFtr9xAo_rd1KJT7kUW2kA4nB1FRdeCrFknizKw98IYWoNNYhJteFEXY27R0iRWxc9NDLij8cSq1Kf9uSDGhbZ5z_d-i7IWyNswJjxheGuze9Q%3Dw2400" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ew9jUMQv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh3.googleusercontent.com/anWdkKav-audI4f6V0YyVG4mbzQaPFtr9xAo_rd1KJT7kUW2kA4nB1FRdeCrFknizKw98IYWoNNYhJteFEXY27R0iRWxc9NDLij8cSq1Kf9uSDGhbZ5z_d-i7IWyNswJjxheGuze9Q%3Dw2400" alt="Picture of the spreadsheet"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Not everything is included here because I had more columns than would fit in this screenshot (the user model was next!), but this was the gist of it. Using this way of thinking through my project, I was able to save myself a lot of time and headache figuring out what to do next, and I'm really happy with how the final (for now!) app turned out. &lt;/p&gt;

&lt;p&gt;I hope this is helpful as an illustration of the method for anyone interested in trying it out! &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Et7svCAb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/hso1y0h1825f9kxd7yyc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Et7svCAb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/hso1y0h1825f9kxd7yyc.png" alt="Picture of the PokéTeam Builder App"&gt;&lt;/a&gt;&lt;br&gt;
(The current version of the app!)&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>productivity</category>
      <category>javascript</category>
      <category>codenewbie</category>
    </item>
    <item>
      <title>A social media strategy for folks afraid of social media</title>
      <dc:creator>Sam Markham</dc:creator>
      <pubDate>Fri, 26 Jun 2020 19:33:35 +0000</pubDate>
      <link>https://forem.com/sharkham/a-social-media-strategy-for-folks-afraid-of-social-media-1gn3</link>
      <guid>https://forem.com/sharkham/a-social-media-strategy-for-folks-afraid-of-social-media-1gn3</guid>
      <description>&lt;p&gt;I'm not sure why, but I really don't like posting things on social media. I find the idea of it stressful, and sometimes sort of scary! Usually the advice I hear at this point is to just avoid it, or to pick one you like and do it well, but, since I've gotten serious at looking for my first developer job, I have realized I really can't avoid social media completely. And, even though it's not exactly necessary*, it would probably be helpful for me to be on more than one platform. &lt;/p&gt;

&lt;p&gt;So, last week I started to troubleshoot this. If I couldn't avoid social media completely, I needed to find some way to engage with it that I was comfortable with. &lt;/p&gt;

&lt;p&gt;So what &lt;em&gt;did&lt;/em&gt; make me uncomfortable with social media? I still haven't quite figured this one out, but thinking more deeply about it made me realize I wasn't uncomfortable with &lt;em&gt;all&lt;/em&gt; social media, or with all kinds of posts either. This seemed like a good place to start. What kinds of things made me really uncomfortable, versus what kinds of things did I feel better about, and how could I work within this? &lt;/p&gt;

&lt;p&gt;I started by thinking about what sorts of things I usually post on the social media I do use (mostly cat pictures), and then, working from there, &lt;strong&gt;made a chart&lt;/strong&gt; to nail this down further: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LQ3_JguN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh3.googleusercontent.com/6F49JJ0SuDfwoUBwLV8xhJ5DfsgO7_J2CcBeWr-blsoUmxrxKUASMohU2PGzejwE-4ti2DdN-vNYfzYNm2dMEBhV-eIQqGAxLXLoLnOmXie8CKrcQg1XcAGLS9yMejqFezRoq7J3OQ%3Dw2400" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LQ3_JguN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh3.googleusercontent.com/6F49JJ0SuDfwoUBwLV8xhJ5DfsgO7_J2CcBeWr-blsoUmxrxKUASMohU2PGzejwE-4ti2DdN-vNYfzYNm2dMEBhV-eIQqGAxLXLoLnOmXie8CKrcQg1XcAGLS9yMejqFezRoq7J3OQ%3Dw2400" alt="Social media chart"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This was a start! It skewed more heavily towards detail in things I'm (mostly) fine posting about, but that's because that's what I really wanted there: ideas for (coding-related!) things to post about on social media. The other important part of this process for me was to take certain things off the table. For example, if posting about things that are too personal makes me uncomfortable? Fine, I could put it in the "nope" column and then stop worrying about it.&lt;/p&gt;

&lt;p&gt;From here, I made a list of relevant things I could post about, and on what platforms, to give myself a more specific idea of what to do going forward. Another thing that makes me reluctant to post is uncertainty about what I'm going to post, so the goal was to take that off the table. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My list&lt;/strong&gt;: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;technical blog posts (Dev.to, with cross-post to Twitter and LinkedIn)&lt;/li&gt;
&lt;li&gt;posts highlighting coding projects (Dev.to, Twitter, LinkedIn)&lt;/li&gt;
&lt;li&gt;challenges like #100daysofCode (Twitter, possibly blog about experience and cross-post)&lt;/li&gt;
&lt;li&gt;answering code questions (Twitter, Dev.to)&lt;/li&gt;
&lt;li&gt;engaging on coding-related posts (Dev.to, LinkedIn, Twitter)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And that's it! So far. The next step, of course, is to increase the frequency of all of these things, and to test my theory that I'll be able to do this without spending too much time worrying about what I'm posting--my usual stumbling block. And then, of course, analyze, reiterate, and test again. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The steps again&lt;/strong&gt;, for anyone else interested in trying this: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Think about what makes you uncomfortable about engaging with social media&lt;/li&gt;
&lt;li&gt;Make a chart of types of engagement that make you more/less comfortable&lt;/li&gt;
&lt;li&gt;Make a list of comfortable/mostly comfortable things to have on hand for when you're telling yourself 'I need to post something' but are stuck on the pressure of doing it&lt;/li&gt;
&lt;li&gt;Probably have some sort of schedule for creating content (that one is a work in progress for me!)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I hope this is helpful to anyone else trying to push themselves in this direction! (And, just for fun, here is another picture of my cat!)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yHHvfQ7L--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh3.googleusercontent.com/z4t1xVEwOAV03Rz8Kx_TiuSyUIUhRbU890GQ-SiZNzZlfCusniIDJ0RxRi9kMZqtzUPbzx7EuJtJHzRvfG68AqiNFnXEcCZTisNNavLu1iPbDQlPBNaNjBIh07H3Cb8-tqjMJRBk0w%3Dw2400" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yHHvfQ7L--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh3.googleusercontent.com/z4t1xVEwOAV03Rz8Kx_TiuSyUIUhRbU890GQ-SiZNzZlfCusniIDJ0RxRi9kMZqtzUPbzx7EuJtJHzRvfG68AqiNFnXEcCZTisNNavLu1iPbDQlPBNaNjBIh07H3Cb8-tqjMJRBk0w%3Dw2400" alt="cat picture"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;*Seriously, I have heard from many folks who know a lot more than I do that if you're not going to do the social media thing you might as well just not have it, as long as your online presence is consistent and professional and doesn't have abandoned handles, but, I am trying this anyway!&lt;/p&gt;

</description>
      <category>jobsearch</category>
      <category>career</category>
    </item>
    <item>
      <title>6 things I learned from my first mock technical interview</title>
      <dc:creator>Sam Markham</dc:creator>
      <pubDate>Thu, 18 Jun 2020 21:24:25 +0000</pubDate>
      <link>https://forem.com/sharkham/6-things-i-learned-from-my-first-mock-technical-interview-3dbi</link>
      <guid>https://forem.com/sharkham/6-things-i-learned-from-my-first-mock-technical-interview-3dbi</guid>
      <description>&lt;p&gt;Since finishing my software engineering bootcamp at Flatiron School last month, I've spent the bulk of my coding time getting ready for the job search, and last week that involved doing a mock technical interview through &lt;a href="https://www.skilledinc.com/"&gt;Skilled&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Despite reading all I could find about technical interviews, and going through whatever concepts the program and the articles I could find said to study, I wasn't quite sure what to expect. &lt;/p&gt;

&lt;p&gt;Post-mock interview, these were my biggest takeaways: &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Talk/write your way through it&lt;/strong&gt;&lt;br&gt;
One of the things my interviewer mentioned I did well in the interview process was that I understood what was being asked of me. I was able to convey this understanding by talking through the problem out loud and also writing down the steps of what I wanted my code to do. All I have read about technical interviews says that interviewers want to know about your ability to problem solve, and your process, and this was definitely my experience! &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Knowledge of algorithms and data structures is helpful even when you're not asked about it&lt;/strong&gt; &lt;br&gt;
In preparation for my technical interview, I spent a lot of time studying things like different data structures, Big O Notation, and algorithms I hadn't encountered much of during my course but were recommended knowledge for interviews. &lt;/p&gt;

&lt;p&gt;I didn't get asked about any of this directly during my technical interview--I had the whole hour to solve one (complicated!) JavaScript problem--but it was relevant anyway. &lt;/p&gt;

&lt;p&gt;At one point, I came across part of the problem where I could have dealt with things by &lt;code&gt;split()&lt;/code&gt;-ing a string into an array, altering each element separately, and then &lt;code&gt;join()&lt;/code&gt;-ing them together as a string again. I decided not to, because that would further increase the algorithm complexity, and found a way to alter the string while keeping it a string instead. &lt;/p&gt;

&lt;p&gt;When I explained this reasoning to my interviewer, they said it was a point in my favour: that I coded that part of the solution in a way that didn't increase algorithm complexity further, but also that I did it deliberately and knew why that choice was a good idea. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Find out in advance what the interviewer's policy is on asking for help/using the documentation)&lt;/strong&gt;&lt;br&gt;
Where I started to get tripped up in my interview was when I reached the point of the coding problem where I would usually think, "ehhhhhh drat I know there is a function that does this, I am going to google and find out what it is." I asked my interviewer if it was alright if I looked up the documentation, and they let me know that was fine, or I could just ask them. This was a big help in progressing through the interview, so I'm very glad I asked! With that said: &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Stick to what you know (and brush up on your functions!)&lt;/strong&gt;&lt;br&gt;
I spent a lot of time during my interview trying to remember &lt;em&gt;the right&lt;/em&gt; function to solve the problem, because I knew there was one, and it just wasn't coming to mind. While this was true, there were better functions than the ones I was using, trying to remember these functions wasted valuable time, and, as my interviewer later pointed out to me in feedback, it would have been fine to say "I know there are better functions to use in this situation but I'm just going to do this for now." So, there are two other key things here:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Prioritize&lt;/strong&gt;&lt;br&gt;
The interviewer wants you to solve the problem in front of you, or at least as much of it as you can. Another way I stumbled in my interview was spending too much time trying to figure out the specific &lt;em&gt;right&lt;/em&gt; way to solve one part and not leaving enough time for the rest of the problem. Pausing to think more about what was important in the problem, and maybe a, "let's assume I have a helper function that does this" would have saved me a lot of time in my interview. And, finally: &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Be confident (/you are a job seeker, not a student)&lt;/strong&gt;&lt;br&gt;
The main thing I forgot during my technical interview was: I'm not a student anymore. As a student it's alright to say, "I know there's a better function for this, can you remind me of what it is?", but as a job seeker, you need to project confidence. Focus on the parts of the question in front of you that you can solve, use methods you're confident with--overall, employers want to see your problem solving skills but they also want to know you can handle yourself without handholding. It's okay to admit to not knowing something if asked directly, or if it comes up, but overall, you'll look like a much stronger interview candidate if you can solve the problem in front of you showing off what skills you do have (and what things you do remember) instead. &lt;/p&gt;

&lt;p&gt;During my mock technical interview I learned a lot of valuable information about what to do and what not to do during the real thing, and I have spent the past few days putting my skills to the test finishing my solution to the problem I was given during the interview--and then solving it again in a better and more efficient manner. (It was a tough one!)&lt;/p&gt;

&lt;p&gt;The one thing I wish I did more of before the interview itself? &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Practice, practice, practice!&lt;/strong&gt; &lt;br&gt;
As well as studying up on algorithms, data structures and functions/methods of whatever language you're interviewing in, a good way to prepare for a technical interview is to practice solving problems in that language. &lt;a href="https://www.codewars.com/"&gt;CodeWars&lt;/a&gt; has been helpful to me so far, I have heard good things about &lt;a href="http://www.hackerrank.com"&gt;HackerRank&lt;/a&gt; and &lt;a href="https://codesignal.com/"&gt;CodeSignal&lt;/a&gt;, and there are many others out there. &lt;/p&gt;

&lt;p&gt;I hope this is helpful to anyone else looking at starting interviews, and good luck! &lt;/p&gt;

</description>
      <category>career</category>
      <category>jobsearch</category>
      <category>codenewbie</category>
    </item>
    <item>
      <title>Follow-up: deploying Forgot/Reset Password (React/Rails app)</title>
      <dc:creator>Sam Markham</dc:creator>
      <pubDate>Mon, 15 Jun 2020 19:28:40 +0000</pubDate>
      <link>https://forem.com/sharkham/follow-up-deploying-forgot-reset-password-react-rails-app-223j</link>
      <guid>https://forem.com/sharkham/follow-up-deploying-forgot-reset-password-react-rails-app-223j</guid>
      <description>&lt;p&gt;&lt;em&gt;(originally posted May 22, 2020)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In my recent &lt;a href="https://dev.to/sharkham/forgot-reset-password-functionality-with-rails-and-react-and-redux-4kp0"&gt;post on forgot/reset password functionality&lt;/a&gt;, I explored setting up a forgot/reset password function with a Rails backend and React frontend, and in my &lt;a href="https://dev.to/sharkham/deploying-a-react-with-redux-rails-app-with-netlify-heroku-5b89"&gt;post on deploying this project with Netlify and Heroku&lt;/a&gt;, I focused on what it took to get the app up and running in production, but there wasn't really room for getting into what that meant for my forgot password functionality. This is what I'll explore here. &lt;/p&gt;

&lt;p&gt;Assuming everything is set up so your forgot/reset password function works in development, and assuming everything else works in deployment, there are only a few changes to make to your app to get the forgot/reset password function working in production. Testing this was the last phase of making sure my Novel Tracker app worked in production--I needed to make sure future users would be able to reset any forgotten passwords successfully before I let the novel contest know the app was ready for use! &lt;/p&gt;

&lt;p&gt;*&lt;em&gt;On the backend *&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;First, in the Rails API, there are some settings to be added to &lt;code&gt;config/environments/production.rb&lt;/code&gt;:&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="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;action_mailer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;perform_caching&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;

  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;action_mailer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;perform_deliveries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;action_mailer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raise_delivery_errors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;


  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;action_mailer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;delivery_method&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;:smtp&lt;/span&gt;
  &lt;span class="n"&gt;host&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"PRODUCTION_URL"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;action_mailer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;default_url_options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;host: &lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;# SMTP settings for gmail&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;action_mailer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;smtp_settings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="ss"&gt;:address&lt;/span&gt;              &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"smtp.gmail.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;:port&lt;/span&gt;                 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;587&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;:user_name&lt;/span&gt;            &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"GMAIL_ACCOUNT"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="ss"&gt;:password&lt;/span&gt;             &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"GMAIL_PASSWORD"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="ss"&gt;:authentication&lt;/span&gt;       &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"plain"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;:enable_starttls_auto&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These are all of my settings in this file that have to do with &lt;code&gt;config.action_mailer&lt;/code&gt;. Most of these should be the same as what you have in &lt;code&gt;config/environments/development.rb&lt;/code&gt; except for these two:&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="n"&gt;host&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"PRODUCTION_URL"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; 
  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;action_mailer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;default_url_options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;host: &lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The host is no longer &lt;code&gt;localhost:3000&lt;/code&gt;, it is whatever your production URL is. &lt;/p&gt;

&lt;p&gt;(For more information on set up with Action Mailer and Gmail, see &lt;a href="https://dev.to/morinoko/sending-emails-in-rails-with-action-mailer-and-gmail-35g4"&gt;this article&lt;/a&gt;.)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;On Heroku&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;Once &lt;code&gt;production.rb&lt;/code&gt; is set up, the only other thing that needs to be configured is the environment variables in Heroku, referred to there as config vars. Navigate to your app on Heroku, go to the settings tab, and add these variables in the config vars section:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PRODUCTION_URL: https://your-app-api.herokuapp.com
GMAIL_ACCOUNT: your.app.gmail.account@gmail.com
GMAIL_PASSWORD: yourgmailapppassword
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;PRODUCTION_URL&lt;/code&gt;, your app's URL on Heroku, is the &lt;code&gt;host&lt;/code&gt; for Action Mailer, and your &lt;code&gt;GMAIL_ACCOUNT&lt;/code&gt; and password are whatever email address you want your app to send the reset password emails from. The &lt;code&gt;GMAIL_ACCOUNT&lt;/code&gt; and &lt;code&gt;GMAIL_PASSWORD&lt;/code&gt; can be the same variables in development and production, but, as Heroku can't access your environment variables in the &lt;code&gt;.env&lt;/code&gt; file listed in your &lt;code&gt;.gitignore&lt;/code&gt;, you need to provide them to Heroku directly as config vars for them to work in production. &lt;/p&gt;

&lt;p&gt;Along these lines, we need to add one more config var to Heroku to get everything working:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RESET_PASSWORD: https://your-app-frontend.netlify.app/reset_password
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a link to the &lt;code&gt;reset_password&lt;/code&gt; page/component in your Netlify app, and Heroku needs to know about it because it's what will be sent out to users in reset password emails. But, there's one final thing that needs to be fixed to get this link working. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;On the frontend&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;React uses client-side routing, which means, basically, it makes one request to the server to grab everything when you first navigate to the app, and after that, when clicking a link made with &lt;code&gt;react-router-dom&lt;/code&gt;, the URL changes locally, and so does the component being displayed, if you've set things up that way--no other request to the server is fired. &lt;/p&gt;

&lt;p&gt;There are multiple workarounds to this, many of them detailed &lt;a href="https://stackoverflow.com/questions/27928372/react-router-urls-dont-work-when-refreshing-or-writing-manually"&gt;here&lt;/a&gt;, but if you're using &lt;code&gt;create-react-app&lt;/code&gt; and Netlify, there's an easy one that works for at least some components.&lt;/p&gt;

&lt;p&gt;Create a &lt;code&gt;public/_redirects&lt;/code&gt; file in the main directory of your front end app, and include this code in it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/*  /index.html  200
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(&lt;a href="https://create-react-app.dev/docs/deployment/#netlify-https-wwwnetlifycom"&gt;Source&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;This will enable users to navigate directly to your &lt;code&gt;/reset_password&lt;/code&gt; link, and any other links (at least in my project) that don't use conditional rendering to only allow logged-in users to see them. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;That's it!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is everything I did to get this part of the app working! There are other tutorials on all of this out there that I've found very useful, but this concludes my series of blog posts on this particular use case, to help myself remember what I did and in case anyone else finds these specific steps useful! (And &lt;a href="http://novel-tracker-app.netlify.app/"&gt;this is the demo version of the app in question&lt;/a&gt;, for any readers who wants to see some of it working on the frontend!)&lt;/p&gt;

</description>
      <category>react</category>
      <category>rails</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Deploying a React (with Redux)/Rails app with Netlify/Heroku</title>
      <dc:creator>Sam Markham</dc:creator>
      <pubDate>Mon, 15 Jun 2020 19:18:09 +0000</pubDate>
      <link>https://forem.com/sharkham/deploying-a-react-with-redux-rails-app-with-netlify-heroku-5b89</link>
      <guid>https://forem.com/sharkham/deploying-a-react-with-redux-rails-app-with-netlify-heroku-5b89</guid>
      <description>&lt;p&gt;&lt;em&gt;(originally posted May 15, 2020)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;My final Flatiron School &lt;a href="http://novel-tracker-app.netlify.app/"&gt;project&lt;/a&gt; was made for use by an online novel contest, and after the project deadline, I set my own deadline of when I told the contest's organizer I would have the live website up by. This led to a lot of quick research and troubleshooting how to get a React/Rails app hosted, and another two weeks after submitting my final project, I had it up! The experience of seeing something I made in use has been very gratifying, and this blog post is an overview of what worked for me, in case it helps someone else!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Initial set-up&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To start off, my app is built with React.js, Redux, and Rails 6. I built the front-end in a &lt;code&gt;project-name-frontend&lt;/code&gt; folder with &lt;code&gt;create-react-app&lt;/code&gt; and the backend in a &lt;code&gt;project-name-backend&lt;/code&gt; with &lt;code&gt;rails new project-name-api --api --database=postgresql&lt;/code&gt;. The frontend and backend are both hooked up to separate github repos. I'm also working off a Mac. This blog post will assume you have a similar setup in order for it to work as a tutorial! &lt;/p&gt;

&lt;p&gt;To hook up your Rails backend to Heroku, it is important it use Postgres instead of SQLite3 (the default) as a database. Adding the &lt;code&gt;--database=postgresql&lt;/code&gt; takes care of a lot of the set up within your Rails project if you include this when you're building it, but I found &lt;a href="https://medium.com/@noordean/setting-up-postgresql-with-rails-application-357fe5e9c28"&gt;this article&lt;/a&gt; helpful too for getting Postgres installed on my machine. You can skip a few of the steps if you start your Rails project setting the database to Postgres, but the rest of it still applies. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deploying the backend&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;Alright, so you've built your React/Rails project, you have a Postgres database in Rails, everything is working in development, you're ready to deploy! &lt;/p&gt;

&lt;p&gt;The first step is getting your backend up on Heroku. Start by making an account on Heroku, and then navigate to &lt;a href="https://devcenter.heroku.com/articles/getting-started-with-rails6"&gt;this tutorial&lt;/a&gt;. It will prompt you to &lt;a href="https://devcenter.heroku.com/articles/heroku-cli#download-and-install"&gt;install the Heroku CLI&lt;/a&gt;, log in on the command line with &lt;code&gt;heroku login&lt;/code&gt;, and then configure your &lt;code&gt;config/database.yml&lt;/code&gt; file. &lt;/p&gt;

&lt;p&gt;What is on the tutorial works for this, but there is a lot of text there, so for simplicity's sake, this is what worked for me:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nl"&gt;&amp;amp;default&lt;/span&gt;
  &lt;span class="na"&gt;adapter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgresql&lt;/span&gt;
  &lt;span class="na"&gt;encoding&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unicode&lt;/span&gt;
  &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;%= ENV['POSTGRES_USER'] %&amp;gt;&lt;/span&gt;
  &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;%= ENV['POSTGRES_PASSWORD'] %&amp;gt;&lt;/span&gt;
  &lt;span class="na"&gt;pool&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
  &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5000&lt;/span&gt;
  &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;%= ENV['POSTGRES_HOST'] %&amp;gt;&lt;/span&gt;
&lt;span class="na"&gt;development&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="s"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*default&lt;/span&gt;
  &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;%= ENV['POSTGRES_DEVELOPMENT_DB'] %&amp;gt;&lt;/span&gt;
&lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="s"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*default&lt;/span&gt;
  &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;%= ENV['POSTGRES_TEST_DB'] %&amp;gt;&lt;/span&gt;
&lt;span class="na"&gt;production&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="s"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*default&lt;/span&gt;
  &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;%= ENV['POSTGRES_DB'] %&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, this relies on some environmental variables. You should have these in an &lt;code&gt;.env&lt;/code&gt; file and that &lt;code&gt;.env&lt;/code&gt; file added to your &lt;code&gt;.gitignore&lt;/code&gt; so it won't show up when you push to github. &lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POSTGRES_USER='username'
POSTGRES_PASSWORD='password'
POSTGRES_HOST='localhost'
POSTGRES_DEVELOPMENT_DB='app_name_development_db'
POSTGRES_TEST_DB='app_name_test_db'
POSTGRES_DB='app_name_db'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, to deploy your app on Heroku, make sure you're in your &lt;code&gt;project-name-backend&lt;/code&gt; directory and type &lt;code&gt;heroku create&lt;/code&gt;. It should say something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Creating app... done, radiant-sierra-11874
https://radiant-sierra-11874.herokuapp.com/ | https://git.heroku.com/radiant-sierra-11874.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(&lt;a href="https://devcenter.heroku.com/articles/getting-started-with-rails6"&gt;source&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Following along with the Heroku tutorial, you can check that the remote was added to your project correctly by typing &lt;code&gt;git config --list | grep heroku&lt;/code&gt;. If you see &lt;code&gt;fatal: not in a git directory&lt;/code&gt; you're not in the right directory. &lt;/p&gt;

&lt;p&gt;Otherwise, type &lt;code&gt;git push heroku master&lt;/code&gt; to deploy your code. This will give you a long block of text, including some warnings at the end you might want to debug. &lt;/p&gt;

&lt;p&gt;If everything goes well there, you can migrate your database, and seed it if applicable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;heroku run rake db:migrate
heroku run rake db:seed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is an abbreviated run through that pulls out the specific steps from Heroku's "&lt;a href="https://devcenter.heroku.com/articles/getting-started-with-rails6"&gt;Getting Started with Rails 6&lt;/a&gt;" article that worked for me, but I highly recommend the full article for more detail here. &lt;/p&gt;

&lt;p&gt;If all of this is working, you can visit your &lt;a href="https://dashboard.heroku.com/apps"&gt;Heroku dashboard&lt;/a&gt; to see your app. &lt;/p&gt;

&lt;p&gt;In the settings tab, you can change the app's name: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hv3M2by5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh3.googleusercontent.com/7EpbPtat0I_7pHlDTDZJDghQMaDtugIQsKaMdru7lMKea0dlDXeZGmuUPH6VQAe6xkouuaC8_9cjmPEb_XcvSptV61HtGRsyKr2_UKXP81FTzCDp32o1lDqhUzeRQC4S__xnmZx0vw%3Dw2400" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hv3M2by5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh3.googleusercontent.com/7EpbPtat0I_7pHlDTDZJDghQMaDtugIQsKaMdru7lMKea0dlDXeZGmuUPH6VQAe6xkouuaC8_9cjmPEb_XcvSptV61HtGRsyKr2_UKXP81FTzCDp32o1lDqhUzeRQC4S__xnmZx0vw%3Dw2400" alt="Screenshot of Heroku settings"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And in the deploy tab, you can connect your app to your Github repo so pushing changes there will push changes to the live Heroku app as well: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ozX3-o61--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh3.googleusercontent.com/kZYGZrNNKFtc0f3QOdBoIEkYbQHy-Zya7IfiC25Y2tLoqXipOGHboG5BDXdWVJi_wyVkBpw1HdIuyYZtGqdkMIfn9ME-ncZxeLpYgRycKheLg_15N-fMlu3wGx3_Vq_qb9j7rQH1Wg%3Dw2400" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ozX3-o61--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh3.googleusercontent.com/kZYGZrNNKFtc0f3QOdBoIEkYbQHy-Zya7IfiC25Y2tLoqXipOGHboG5BDXdWVJi_wyVkBpw1HdIuyYZtGqdkMIfn9ME-ncZxeLpYgRycKheLg_15N-fMlu3wGx3_Vq_qb9j7rQH1Wg%3Dw2400" alt="Screenshot of Heroku deploy settings"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deploying the frontend&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;We'll get back to the backend later, but the next step to getting this app hooked up is deploying your React app through &lt;a href="https://www.netlify.com/"&gt;Netlify&lt;/a&gt;. It's possible to do the frontend through Heroku too of course, but I like Netlify for the frontend because it loads right away when you navigate to it. In the free version of Heroku, your server sleeps when it hasn't been pinged for a while, so hosting your frontend on Netlify will show your user the front page of your site right away while Heroku gets the backend up and running in the background. (With that said, I recommend using a fetch in &lt;code&gt;componentDidMount&lt;/code&gt; on your React app's &lt;code&gt;App.js&lt;/code&gt; component, or whatever else loads first, so that the Heroku server gets booted starting from when a user first gets to your site.)&lt;/p&gt;

&lt;p&gt;To get started with Netlify, create an account then click to "New Site from Git". Clicking on "Github" from the list of options will let you search your Github repos for &lt;code&gt;project-name-frontend&lt;/code&gt;. The settings on the next page are fine, you can go ahead and "Deploy site" from there. Netlify has a &lt;a href="https://www.netlify.com/blog/2016/09/29/a-step-by-step-guide-deploying-on-netlify/"&gt;blog post&lt;/a&gt; with an overview of this process, featuring more screenshots, that I found helpful as well! &lt;/p&gt;

&lt;p&gt;Once your Netlify app is up and running, you can change its name in the "General" section of settings, and then navigate to the "Build and Deploy" tab. Make sure the site is set up for continuous deployment, the first section there, and then scroll down to environment. &lt;/p&gt;

&lt;p&gt;Set an environment variable with a key of something like this: &lt;code&gt;REACT_APP_BASE_API_URL&lt;/code&gt;, and set the value to the URL of your new Heroku app. &lt;/p&gt;

&lt;p&gt;The thing that I found when deploying my app is: running it on my local server in development, it uses environment variables from my &lt;code&gt;.env&lt;/code&gt; file. Running it in production from Heroku and Netlify, the frontend and backend apps don't have access to any of these variables, so they have to be set through the Heroku and Netlify dashboards. This is actually &lt;em&gt;great&lt;/em&gt;, because it's an easy way of making sure your frontend fetches from &lt;code&gt;localhost:3000&lt;/code&gt; (or whatever port your backend is on in) in development and from &lt;code&gt;project-name-backend.heroku.app&lt;/code&gt; in production, but it takes some configuring. &lt;/p&gt;

&lt;p&gt;In &lt;code&gt;project-name-frontend&lt;/code&gt;, go into all of your files that make fetch requests. Change your base URL for these fetch requests to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;baseURL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;REACT_APP_BASE_API_URL&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In React apps, environment variables are accessed through &lt;code&gt;process.env&lt;/code&gt;, and if you've made your app with &lt;code&gt;create-react-app&lt;/code&gt;, all environment variables need to be prefaced by &lt;code&gt;REACT_APP_&lt;/code&gt; to work properly. (&lt;a href="https://medium.com/@tacomanator/environments-with-create-react-app-7b645312c09d"&gt;More information here&lt;/a&gt;!)&lt;/p&gt;

&lt;p&gt;From here, make an &lt;code&gt;.env.development&lt;/code&gt; file in your &lt;code&gt;project-name-frontend&lt;/code&gt; directory, add it to your &lt;code&gt;.gitignore&lt;/code&gt; file, and add this environment variable there:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;REACT_APP_BASE_API_URL='http://localhost:3000/'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This should enable your frontend to properly &lt;code&gt;fetch&lt;/code&gt; from your backend, from the local server in development and your heroku app in production! &lt;/p&gt;

&lt;p&gt;But, there's a problem here--the backend doesn't know to accept requests from your Netlify frontend yet! We have to go back and do more configuration there. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;A note about Netlify:&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Before I go any further, I want to briefly mention that while Netlify loads faster than Heroku when first navigating to the live site, Netlify is definitely slower than Heroku to update after you've run &lt;code&gt;git push&lt;/code&gt; and pushed changes through to it. I ran into a lot of issues debugging in deployment just because Netlify hadn't loaded the (working!) update I'd made to my code yet. &lt;/p&gt;

&lt;p&gt;So if you are refreshing your Netlify frontend to see if something has worked, you might need to wait a few minutes for the update to take! &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;More backend configuration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Assuming your app was working in development before this, you should have your &lt;code&gt;/config/initializers/cors.rb&lt;/code&gt; file configured. The &lt;code&gt;cors&lt;/code&gt; file is where we tell the backend what requests to accept, so this is what needs to be reconfigured to get the Heroku app to accept &lt;code&gt;fetch&lt;/code&gt; requests from the Netlify app.&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="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;middleware&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insert_before&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Rack&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Cors&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;allow&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;origins&lt;/span&gt; &lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'FRONT_END_URL'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="n"&gt;resource&lt;/span&gt; &lt;span class="s1"&gt;'*'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;headers: :any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;methods: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:get&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:post&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:put&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:patch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:delete&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:head&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="ss"&gt;credentials: &lt;/span&gt;&lt;span class="kp"&gt;true&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;p&gt;Setting the &lt;code&gt;cors&lt;/code&gt; file to allow origins &lt;code&gt;ENV['FRONT_END_URL']&lt;/code&gt; means it will allow requests from whatever that environment variable is set to in &lt;code&gt;.env&lt;/code&gt; in development, and whatever that environment variable is set to on Heroku in production. &lt;/p&gt;

&lt;p&gt;Add this line to your &lt;code&gt;.env&lt;/code&gt; file (assuming you've set your port to 3001 on the frontend like I did):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FRONT_END_URL='http://localhost:3001'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the Heroku dashboard, go into settings, down to Config Vars, and create a new &lt;code&gt;FRONT_END_URL&lt;/code&gt; config variable and set it to your Netlify app URL. &lt;/p&gt;

&lt;p&gt;Remember, modifications to the &lt;code&gt;cors.rb&lt;/code&gt; file mean you need to restart your Rails server on the backend, and also, the change may take a minute or two to take effect in your Heroku app file too. &lt;/p&gt;

&lt;p&gt;But, this is it! Both apps have been deployed, and should be working properly! &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Redux issue&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Or, so I thought until I proudly sent the link to my website to the novel contest's organizer only to hear my beautiful app was only showing a blank page. Some poking around on my end trying to pull up the app in different browsers revealed I had the same problem too: the app was only displaying properly in Chrome. &lt;/p&gt;

&lt;p&gt;Eventually I figured it out: Redux Devtools, which were amazingly helpful while putting together my app, somehow meant that the app wasn't visible for any browser that did not have the devtools installed. I'm sure there's a way of configuring this so the devtools are included in development and not in production, but facing a deadline, I just removed them and everything worked fine. &lt;/p&gt;

&lt;p&gt;My code for creating my Redux store went from this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createStore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rootReducer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;compose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;applyMiddleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;thunk&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;__REDUX_DEVTOOLS_EXTENSION__&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;__REDUX_DEVTOOLS_EXTENSION__&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createStore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rootReducer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;applyMiddleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;thunk&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And everything worked! &lt;/p&gt;

&lt;p&gt;I hope this is helpful to anyone else looking to deploy React/Rails apps! &lt;/p&gt;

</description>
      <category>react</category>
      <category>rails</category>
      <category>redux</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Forgot/Reset Password functionality with Rails and React (and Redux)</title>
      <dc:creator>Sam Markham</dc:creator>
      <pubDate>Mon, 15 Jun 2020 19:11:31 +0000</pubDate>
      <link>https://forem.com/sharkham/forgot-reset-password-functionality-with-rails-and-react-and-redux-4kp0</link>
      <guid>https://forem.com/sharkham/forgot-reset-password-functionality-with-rails-and-react-and-redux-4kp0</guid>
      <description>&lt;p&gt;&lt;em&gt;(originally published May 6, 2020)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;For my final project with Flatiron School, I have been building an app that tracks novel writing progress for novel contests. I have actually built it for a specific novel contest, so throughout coding it, I knew my goal once I had finished the basic functionality was to deploy it for use. &lt;/p&gt;

&lt;p&gt;The biggest thing that stuck out to me as necessary for this was forgot/reset password functionality. I could do an admin controller later, fix my styling later, et cetera, but if I had live users and they were forgetting their passwords, this would be a problem. &lt;/p&gt;

&lt;p&gt;Many hours later, I had done enough research and trial and error to build a solution. &lt;/p&gt;

&lt;p&gt;Before I get into it, a bit of a disclaimer--if you're logging in users through a Rails API backend, this is really a Rails issue. There are a few good tutorials on how to do this functionality with only Rails already. I drew heavily from them for my solution (and will link them later!), but what I wasn't able to find was something incorporating React to do this. Honestly, there are &lt;em&gt;good reasons for this&lt;/em&gt;, which I will get into later! But, if you are looking to use Rails only for the backend of password resets and React with Redux for the front end, read on! &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In the Rails API:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;First, some routes:&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="n"&gt;post&lt;/span&gt; &lt;span class="s1"&gt;'api/v1/forgot_password'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"api/v1/passwords#forgot"&lt;/span&gt;
  &lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="s1"&gt;'api/v1/reset_password'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"api/v1/passwords#reset"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are two routes here because first you want the user to be able to submit their email through a forgot password action that will send a code to their email to start the process, and then once they have the code, you want them to be able to use it to submit their new password. &lt;/p&gt;

&lt;p&gt;Next, the user model needs a couple new columns. Whether you set this up through adding them after the fact or putting them directly in the original migration, they should look like this in your schema:&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="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt; &lt;span class="s2"&gt;"password_reset_token"&lt;/span&gt;
    &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;datetime&lt;/span&gt; &lt;span class="s2"&gt;"password_reset_sent_at"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then you need a new controller:&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;Api::V1::PasswordsController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;forgot&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;email: &lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:_json&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;
      &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;json: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="ss"&gt;alert: &lt;/span&gt;&lt;span class="s2"&gt;"If this user exists, we have sent you a password reset email."&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send_password_reset&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="c1"&gt;#this sends regardless of whether there's an email in database for security reasons&lt;/span&gt;
      &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;json: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="ss"&gt;alert: &lt;/span&gt;&lt;span class="s2"&gt;"If this user exists, we have sent you a password reset email."&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;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;reset&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;password_reset_token: &lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:token&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="ss"&gt;email: &lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:email&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;present?&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;password_token_valid?&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reset_password&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:password&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;json: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="ss"&gt;alert: &lt;/span&gt;&lt;span class="s2"&gt;"Your password has been successfuly reset!"&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:user_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;
      &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;json: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;error: &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;full_messages&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="ss"&gt;status: :unprocessable_entity&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;json: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;error:  &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'Link not valid or expired. Try generating a new link.'&lt;/span&gt;&lt;span class="p"&gt;]},&lt;/span&gt; &lt;span class="ss"&gt;status: :not_found&lt;/span&gt;
    &lt;span class="k"&gt;end&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;p&gt;For my app, I have the passwords controller namespaced under &lt;code&gt;Api::V1&lt;/code&gt;, but this is just preference. As long as the namespace is the same in the route and the controller (and the controller is under the proper &lt;code&gt;Api&lt;/code&gt; and then &lt;code&gt;V1&lt;/code&gt; folders, if applicable), it'll work. &lt;/p&gt;

&lt;p&gt;A lot of this code is drawn from these tutorials (&lt;a href="https://www.sitepoint.com/handle-password-and-email-changes-in-your-rails-api/"&gt;one&lt;/a&gt;, &lt;a href="http://railscasts.com/episodes/274-remember-me-reset-password?view=asciicast"&gt;two&lt;/a&gt;), so I won't get too deep into specifics, but I recommend reading them if you're deploying this to get a better understanding of exactly what's going on! &lt;/p&gt;

&lt;p&gt;Briefly, the important thing about the &lt;code&gt;forgot&lt;/code&gt; action is that you're finding the user by the email param that the user has submitted through a form (we'll get there), and then sending an email regardless of whether the email is in the database for security reasons, &lt;em&gt;but&lt;/em&gt; letting the user know about this so they don't spend forever waiting for an email only to realize later, oh no that was the wrong email I put in. When testing this, I recommend having different alerts for each case so you know which is which, but for deployment, this is what worked for me. &lt;/p&gt;

&lt;p&gt;The reset method is searching for a user by their email &lt;em&gt;and&lt;/em&gt; the &lt;code&gt;password_reset_token&lt;/code&gt; that firing the &lt;code&gt;forgot&lt;/code&gt; action sets on their account. This is a deviation from the tutorials I used for this part, and I'll get into why later. If the user exists and their token is valid, then the password reset fires, and if that works, they're also logged in by setting the &lt;code&gt;session[:user_id]&lt;/code&gt; to their id. If the token is expired, or doesn't exist, or there's no user by that email, an error is rendered. &lt;/p&gt;

&lt;p&gt;Of course, to make this work we need some methods on the user model!&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;User&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class="o"&gt;...&lt;/span&gt;
  &lt;span class="n"&gt;has_secure_password&lt;/span&gt;
  &lt;span class="n"&gt;validates&lt;/span&gt; &lt;span class="ss"&gt;:password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;length: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;minimum: &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;wrong_length: &lt;/span&gt;&lt;span class="s2"&gt;"Password must be at least 5 characters."&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="ss"&gt;if: :password&lt;/span&gt;

&lt;span class="o"&gt;...&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send_password_reset&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;password_reset_token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;generate_base64_token&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;password_reset_sent_at&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;zone&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;
    &lt;span class="n"&gt;save!&lt;/span&gt;
    &lt;span class="no"&gt;UserMailer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;password_reset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;deliver_now&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;password_token_valid?&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;password_reset_sent_at&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="nf"&gt;hour&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;zone&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&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;reset_password&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;password_reset_token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;
    &lt;span class="n"&gt;save!&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

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

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;generate_base64_token&lt;/span&gt;
    &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;SecureRandom&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;urlsafe_base64&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;p&gt;&lt;code&gt;send_password_reset&lt;/code&gt; sets the user's &lt;code&gt;password_reset_token&lt;/code&gt; attribute to a randomly generated token, sets the &lt;code&gt;password_reset_sent_at&lt;/code&gt; to the current time, and then after saving these to the user, sends an email to the user that will include this token and further instructions. More on that soon! The &lt;code&gt;password_token_valid&lt;/code&gt; method checks if the token has been sent within the hour--if it's been longer than an hour, the application won't accept it. This sort of thing is personal preference, I've seen it set to longer than an hour, but I went with a shorter time window for extra security because some of the React implementation is a bit lower security compared to some other ways of doing this. The &lt;code&gt;reset_password&lt;/code&gt; method sets the token to &lt;code&gt;nil&lt;/code&gt; so that once it's used once to reset the password it can't be reset again, and it changes the user's password to whatever they entered in the form. &lt;/p&gt;

&lt;p&gt;The password validation line is &lt;em&gt;important&lt;/em&gt;--without this you won't be able to set the &lt;code&gt;password_reset_token&lt;/code&gt; and &lt;code&gt;password_reset_sent_at&lt;/code&gt;. For more information on why, I have a separate blog post on that &lt;a href="https://dev.to/sharkham/changing-attributes-on-a-user-in-rails-without-having-to-re-enter-password-5bgb"&gt;here&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;The next thing to get set up is the Mailer functionality. First we need to generate a mailer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rails g mailer user_mailer password_reset
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create a &lt;code&gt;user_mailer.rb&lt;/code&gt; file under mailers, and two views for the &lt;code&gt;password_reset&lt;/code&gt; email. This code goes in &lt;code&gt;UserMailer&lt;/code&gt;--it's the method you're calling in &lt;code&gt;send_password_reset&lt;/code&gt;:&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;UserMailer&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationMailer&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;password_reset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;
    &lt;span class="n"&gt;mail&lt;/span&gt; &lt;span class="ss"&gt;to: &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;subject: &lt;/span&gt;&lt;span class="s2"&gt;"Password Reset"&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;p&gt;The two views generated with the terminal command are really just html and plain text versions of the same email, and  your code for both of them should be the same other than that one can use html tags.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erb"&gt;&lt;code&gt;Hi &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="vi"&gt;@user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;,

You are receiving this email because you have requested a password reset for your Novel Tracker account.

Please use this code to reset your password: &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="vi"&gt;@user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;password_reset_token&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;

This code will expire one hour from password reset request.

To reset your password please enter your code in the form here: http://localhost:3001/reset_password

If you did not request your password to be reset please ignore this email and your password will stay as it is.

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

&lt;/div&gt;



&lt;p&gt;You can use ERB tags to put in the user's name (or username, if your app uses that instead), and, importantly, the token. &lt;/p&gt;

&lt;p&gt;This is where my code diverges a little. &lt;a href="http://railscasts.com/episodes/274-remember-me-reset-password?view=asciicast"&gt;This tutorial&lt;/a&gt; shows how to create a reset password view, and, even though the example there is done in a Rails-only project, many single page applications are not completely single page, and do something similar to this too--a reset password view through the API, and the rest of the app through the front end. &lt;/p&gt;

&lt;p&gt;Because I'm stubborn, and because I didn't want to figure out how to style a page rendered through Rails the same way I'd styled my React frontend, I decided to try to figure out how to do this through React instead. This led to a few specific choices here: &lt;/p&gt;

&lt;p&gt;One: exposing the password token in the email instead of including it as part of a dynamically generated link for the user to follow. Some apps do have both options, but mine only has one, because I wanted it to happen through a static link in React. This is because React is a bit strange about links. Well, not strange, but because it uses Client-side routing instead of Server-side routing, basically all of the app's content loads on the initial GET request to the server, and all of the routing from then is moving around within pages that are already downloaded from the beginning. &lt;/p&gt;

&lt;p&gt;There are ways around this--this &lt;a href="https://stackoverflow.com/questions/27928372/react-router-urls-dont-work-when-refreshing-or-writing-manually"&gt;stack overflow thread&lt;/a&gt; gets into some. The specifics of figuring that out is beyond the scope of this blog post, but for my app, I have configured things so that any links a user doesn't need to be logged in to access can be navigated to by typing in the URL manually, and everything else (that requires a check for a logged in user) can't be. If you're going to use the way of doing things I'm outlining in this blog post for your project, make sure this is possible in your app!&lt;/p&gt;

&lt;p&gt;Two: including a link to the reset password page. As previously described, if you can get it to work for your React app, it will be cleaner to do it this way and a bit more secure to have it not linked to from your front end. &lt;/p&gt;

&lt;p&gt;Having a static link to a reset password page does, however, make things a little less secure. This is why I have configured mine to require both the correct token and the matching user email in order to reset a user's password. &lt;/p&gt;

&lt;p&gt;Alright! The next step is configuring your settings so the mailing itself will work. REMEMBER: WHEN YOU CHANGE THESE SETTINGS, RESTART YOUR SERVER AFTERWARDS! I am embarrassed to admit this took me a lot of time in testing to figure out, so there is a reminder here! &lt;/p&gt;

&lt;p&gt;In config/environments/development.rb:&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="c1"&gt;#added settings&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;action_mailer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;perform_deliveries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;action_mailer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raise_delivery_errors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;

  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;action_mailer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;delivery_method&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;:smtp&lt;/span&gt;
  &lt;span class="n"&gt;host&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'localhost:3000'&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;action_mailer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;default_url_options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;:host&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'localhost:3000'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;protocol: &lt;/span&gt;&lt;span class="s1"&gt;'http'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;# SMTP settings for gmail&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;action_mailer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;smtp_settings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="ss"&gt;:address&lt;/span&gt;              &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"smtp.gmail.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;:port&lt;/span&gt;                 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;587&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;:user_name&lt;/span&gt;            &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"GMAIL_ACCOUNT"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="ss"&gt;:password&lt;/span&gt;             &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"GMAIL_PASSWORD"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="ss"&gt;:authentication&lt;/span&gt;       &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"plain"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;:enable_starttls_auto&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Most of these settings are from &lt;a href="https://dev.to/morinoko/sending-emails-in-rails-with-action-mailer-and-gmail-35g4"&gt;this article&lt;/a&gt;, and I would recommend reading it as well for more information on how they work and for troubleshooting! Some of the trickier things here: your app needs somewhere to send mail from. This article recommends setting up a dedicated gmail account for this, which has worked for me. I have kept the information about it in my &lt;code&gt;.env&lt;/code&gt; file, which I have added to my &lt;code&gt;.gitignore&lt;/code&gt; file so it won't be uploaded to GitHub when I update my project there. &lt;/p&gt;

&lt;p&gt;The other recommendation from the article that I appreciated was setting up two-factor authentication and then setting an app password for apps to use for the email account--the app password is what I'm calling here with my &lt;code&gt;GMAIL_PASSWORD&lt;/code&gt; variable. When I have tested this, the gmail account I've sent to still puts these emails in the spam folder, but they do go through at least! &lt;/p&gt;

&lt;p&gt;Also check out the previously linked article for advice on settings for your &lt;code&gt;config/environments/production.rb&lt;/code&gt; file. As of writing this post I am still in the process of deploying my backend, so cannot yet speak to what changes work for me there. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In the React front-end&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;For reference, I didn't really code everything in Rails first and then everything in React after--it happened around the same time and involved a lot of testing throughout. But, for the purposes of this post, I thought it would be easier to separate these concerns to show how it works. &lt;/p&gt;

&lt;p&gt;So, with that said, forgot/reset password in React! First, you need a &lt;code&gt;ForgotPassword&lt;/code&gt; component to display the form for users to request the code to their email:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;forgotPassword&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;../helpers/passwords&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Link&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;withRouter&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-router-dom&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;ForgotPassword&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;handleChange&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&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;name&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="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="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;value&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;handleSubmit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&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;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nx"&gt;forgotPassword&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;email&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&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="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Request password reset:&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt; &lt;span class="na"&gt;onSubmit&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handleSubmit&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"forgotpasswordemail"&lt;/span&gt; &lt;span class="na"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handleChange&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Submit&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;withRouter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ForgotPassword&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nmoaSqYu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh3.googleusercontent.com/VyOyXwFwnJivaGDtqAmLNG1NAM2DBi8KK7eirB1BgPLFAm5qyVbujr0udNtNk2yDyVfna3G1KMz4zYJiPaJUpnXUYekLbcMzJj8EjapRmsqPe8gFrJw2L0ulhhbdjDM-zs_1m_3fug%3Dw2400" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nmoaSqYu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh3.googleusercontent.com/VyOyXwFwnJivaGDtqAmLNG1NAM2DBi8KK7eirB1BgPLFAm5qyVbujr0udNtNk2yDyVfna3G1KMz4zYJiPaJUpnXUYekLbcMzJj8EjapRmsqPe8gFrJw2L0ulhhbdjDM-zs_1m_3fug%3Dw2400" alt="ForgotPassword component"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;My ForgotPassword component! Please note this isn't exactly the code above--I removed styling with reactstrap to make the code in this post easier to read.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is a basic class component with a controlled form, but on submit, two important things are happening: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The user's email is submitted to the &lt;code&gt;forgotPassword&lt;/code&gt; method being called from the &lt;code&gt;helpers/passwords.js&lt;/code&gt; file&lt;/li&gt;
&lt;li&gt;The user is being redirected back to the home page with &lt;code&gt;this.props.history.push()&lt;/code&gt;, and this method is possible to use here because of the last line: &lt;code&gt;withRouter(ForgotPassword)&lt;/code&gt;. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In that helpers file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;baseURL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://localhost:3000/api/v1&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;const&lt;/span&gt; &lt;span class="nx"&gt;forgotPassword&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&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;return&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;baseURL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/forgot_password`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;include&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&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;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&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;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&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;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This method sends a &lt;code&gt;POST&lt;/code&gt; request with the user's email to our &lt;code&gt;/forgot_password&lt;/code&gt; route, and when it receives a response, it displays an alert with that response. Going all the way back to our &lt;code&gt;passwords_controller&lt;/code&gt; in the Rails section of this post, that alert is &lt;code&gt;"If this user exists, we have sent you a password reset email."&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The next step of getting this set up in React is the &lt;code&gt;ResetPassword&lt;/code&gt; component to display the form for users to enter the code they have received by email and use it to reset their password:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;resetPassword&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;../helpers/passwords&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;connect&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-redux&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;ResetPassword&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;token&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="na"&gt;email&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="na"&gt;password&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="na"&gt;password_confirmation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;handleChange&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&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;name&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="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="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;value&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;handleSubmit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&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;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;preventDefault&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;password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;password_confirmation&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;password_confirmation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Passwords don't match&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;password&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="na"&gt;password_confirmation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resetPassword&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;token&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="na"&gt;email&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="na"&gt;password&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="na"&gt;password_confirmation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Reset Password:&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt; &lt;span class="na"&gt;onSubmit&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handleSubmit&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt; &lt;span class="na"&gt;for&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"token"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Token:&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"token"&lt;/span&gt; &lt;span class="na"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handleChange&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"token"&lt;/span&gt; &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"token"&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"token"&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;The code that was emailed to you. This is case-sensitive.&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt; &lt;span class="na"&gt;for&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Email:&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="na"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handleChange&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt; &lt;span class="na"&gt;for&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;New password:&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt; &lt;span class="na"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handleChange&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt; &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Set your new password here.&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt; &lt;span class="na"&gt;for&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"password_confirmation"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Confirm new password:&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"password_confirmation"&lt;/span&gt; &lt;span class="na"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handleChange&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"password_confirmation"&lt;/span&gt; &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"password confirmation"&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;password_confirmation&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"secondary"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Reset Password&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mapDispatchToProps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;dispatch&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;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;resetPassword&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;credentials&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;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resetPassword&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;mapDispatchToProps&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="nx"&gt;ResetPassword&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--w4Ot7Rjn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh3.googleusercontent.com/7AvSs2-9Q8Q-CzQbv5AyWOogZmS0te-afTy9xC-mAgya4zDfq-IaQLxXFTZHpXgO3SLETXpG1woKgRjfTt3XF4IfYrTL-DBe8wb-r83ykhtCckVlqxb2dyDj-GBpp5iJ6qamjW7EXA%3Dw2400" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--w4Ot7Rjn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh3.googleusercontent.com/7AvSs2-9Q8Q-CzQbv5AyWOogZmS0te-afTy9xC-mAgya4zDfq-IaQLxXFTZHpXgO3SLETXpG1woKgRjfTt3XF4IfYrTL-DBe8wb-r83ykhtCckVlqxb2dyDj-GBpp5iJ6qamjW7EXA%3Dw2400" alt="ResetPassword component"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;My ResetPassword component! As with the previous screenshot, this also includes reactstrap styling not included in my code here for simplicity's sake.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A little more is going on here! First, in &lt;code&gt;handleSubmit&lt;/code&gt;, an alert fires and the &lt;code&gt;password&lt;/code&gt; and &lt;code&gt;password_confirmation&lt;/code&gt; fields reset to blank values if they don't match, to make sure the user is really resetting their password to the right thing. Second, if everything is in order on the form, &lt;code&gt;resetPassword&lt;/code&gt; fires. &lt;/p&gt;

&lt;p&gt;A bit of a disclaimer on this one: &lt;code&gt;resetPassword&lt;/code&gt; isn't quite what I would consider a Redux action, and I honestly haven't figured out yet whether it's a good idea to put it in an &lt;code&gt;actions&lt;/code&gt; folder, as is Redux convention, or not. I am dispatching it instead of just calling it though, and mapping it to props through &lt;code&gt;mapDispatchToProps&lt;/code&gt; and the &lt;code&gt;connect&lt;/code&gt; function, and this is because after it fires, I want it to fire my &lt;code&gt;getCurrentUser&lt;/code&gt; action and log the user in, and that is a Redux action. &lt;/p&gt;

&lt;p&gt;Here's what that looks like!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getCurrentUser&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;../actions/currentUser&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;baseURL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://localhost:3000/api/v1&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;const&lt;/span&gt; &lt;span class="nx"&gt;forgotPassword&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&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="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resetPassword&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;credentials&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;return&lt;/span&gt; &lt;span class="nx"&gt;dispatch&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;return&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;baseURL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/reset_password`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;include&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&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;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;credentials&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;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&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="o"&gt;!!&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;alert&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="nx"&gt;getCurrentUser&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="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="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;This method sends the credentials submitted in the &lt;code&gt;ResetPassword&lt;/code&gt; component form to the &lt;code&gt;/reset_password&lt;/code&gt; path as a &lt;code&gt;POST&lt;/code&gt; request and returns a response. If there's an error in the action in &lt;code&gt;passwords_controller&lt;/code&gt;, that will be an error, and this will show as an alert on the front end. If things go well on the back end, it show a "your password was reset!" alert and then checks the session for a current user. &lt;/p&gt;

&lt;p&gt;Getting into that functionality is a little beyond the scope of this blog post too, but I will demonstrate this part of my sessions functionality briefly to put the previous code in context: &lt;/p&gt;

&lt;p&gt;routes.rb:&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="n"&gt;get&lt;/span&gt; &lt;span class="s1"&gt;'/api/v1/get_current_user'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"api/v1/sessions#get_current_user"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;application_controller.rb:&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="o"&gt;...&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;current_user&lt;/span&gt;
    &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;id: &lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:user_id&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;def&lt;/span&gt; &lt;span class="nf"&gt;logged_in?&lt;/span&gt;
    &lt;span class="o"&gt;!!&lt;/span&gt;&lt;span class="n"&gt;current_user&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;sessions_controller.rb:&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;def&lt;/span&gt; &lt;span class="nf"&gt;get_current_user&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;logged_in?&lt;/span&gt;
      &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;json: &lt;/span&gt;&lt;span class="n"&gt;current_user&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;p&gt;actions/currentUser.js:&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="p"&gt;...&lt;/span&gt; 
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getCurrentUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;dispatch&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;return&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;baseURL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/get_current_user`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;include&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GET&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&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;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&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;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;setCurrentUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="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;So, the &lt;code&gt;getCurrentUser&lt;/code&gt; action dispatches a &lt;code&gt;GET&lt;/code&gt; request to the &lt;code&gt;get_current_user&lt;/code&gt; action in the &lt;code&gt;sessions_controller&lt;/code&gt;, and if there is currently a user in session--as a user is set in session in the &lt;code&gt;reset&lt;/code&gt; action in the &lt;code&gt;passwords_controller&lt;/code&gt; in the code at the beginning of this post--then it returns the user object, and uses that to set a current user in the Redux store, which, for the purposes of my app, is logging them in. &lt;/p&gt;

&lt;p&gt;As a final note, there is no redirect in my &lt;code&gt;ResetPassword&lt;/code&gt; component because my app has conditional rendering for that page--once a user is logged in, they will be redirected away from routes that logged in users don't need to see anyway.&lt;/p&gt;

&lt;p&gt;Phew! I think that's it. If you've made it this far, thank you for sticking it out, and I hope this helps if you're trying to implement something similar! &lt;/p&gt;

</description>
      <category>rails</category>
      <category>react</category>
      <category>redux</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Changing attributes on a user in Rails without having to re-enter password</title>
      <dc:creator>Sam Markham</dc:creator>
      <pubDate>Mon, 15 Jun 2020 18:56:01 +0000</pubDate>
      <link>https://forem.com/sharkham/changing-attributes-on-a-user-in-rails-without-having-to-re-enter-password-5bgb</link>
      <guid>https://forem.com/sharkham/changing-attributes-on-a-user-in-rails-without-having-to-re-enter-password-5bgb</guid>
      <description>&lt;p&gt;&lt;em&gt;(originally published on May 5, 2020)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This is something that first came up for me during my Rails project for Flatiron, and I really wish I had figured it out then, but it feels fitting to have finally solved this mystery in my last weeks in the program. &lt;/p&gt;

&lt;p&gt;The issue is: you've set up a project with Rails. You include a user model, and add validations for the user's password. Then you want to make something about the user editable. A bio attribute, or something like that. You get all of the edit functionality set up, test out submitting it, and, error! A password hasn't been entered! &lt;/p&gt;

&lt;p&gt;This makes sense, because you didn't enter a password, and you don't want the user to have to enter a password to make changes to their profile when they're already logged in either! But because of the way the password validation is set up, Rails requires a password to be there to save the user. &lt;/p&gt;

&lt;p&gt;To fix this, include this line in your user model:&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="n"&gt;validates&lt;/span&gt; &lt;span class="ss"&gt;:password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;length: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;minimum: &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;wrong_length: &lt;/span&gt;&lt;span class="s2"&gt;"Password must be at least 5 characters."&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="ss"&gt;if: :password&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first bit is specific to my project, though having a minimum password length is probably a good idea anyway, but the last part is what does the job here: &lt;code&gt;if: :password&lt;/code&gt;. This tiny bit of code means that if a password isn't included in the changed user object being submitted, then Rails won't worry about validating it. &lt;/p&gt;

&lt;p&gt;Finally, if you're dealing with strong params, include this in whatever controller is dealing with updating the user:&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;if&lt;/span&gt; &lt;span class="n"&gt;user_params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:password&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;blank?&lt;/span&gt;
  &lt;span class="n"&gt;user_params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This should work for projects with Rails as an API (my current project) as well as projects only using Rails (the project I may go back and add this functionality to!). &lt;/p&gt;

&lt;p&gt;I hope this helps anyone else struggling with this issue as I was! &lt;/p&gt;

</description>
      <category>rails</category>
      <category>codenewbie</category>
    </item>
    <item>
      <title>Dealing with overlapping reducers in Redux</title>
      <dc:creator>Sam Markham</dc:creator>
      <pubDate>Mon, 15 Jun 2020 18:53:17 +0000</pubDate>
      <link>https://forem.com/sharkham/dealing-with-overlapping-reducers-in-redux-dcm</link>
      <guid>https://forem.com/sharkham/dealing-with-overlapping-reducers-in-redux-dcm</guid>
      <description>&lt;p&gt;&lt;em&gt;(originally published April 25, 2020)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;My final project for Flatiron School got a lot more complicated than I was expecting it to with reducers, and there were many little finnicky things here that tripped me up, even into the last hours of coding! This made me think it might be a good thing to write a blog about. &lt;/p&gt;

&lt;p&gt;To get briefly into the context, my project is a novel tracker app where users can keep track of their writing progress when participating in a novel writing contest. The website's functionality depends on having access to: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a current user &lt;/li&gt;
&lt;li&gt;their current novel (from this year, as the contest is meant to run every year)&lt;/li&gt;
&lt;li&gt;all the novels in this year's contest &lt;/li&gt;
&lt;li&gt;(it also includes a few other things, but we don't need to worry about them here!)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With Redux, I could keep all of these things in store, which can be accessed from any component in the app--super handy!--and with Redux's &lt;code&gt;combineReducers&lt;/code&gt; function, I could make a store with different reducers for each key I wanted in store.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;combineReducers&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;redux&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;allCurrentNovels&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;./allCurrentNovels&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;currentUser&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;./currentUser&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;currentNovel&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;./currentNovel&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rootReducer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;combineReducers&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;currentUser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;allCurrentNovels&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;currentNovel&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;The fun part came when I decided I wanted the &lt;code&gt;currentNovel&lt;/code&gt; to also be part of &lt;code&gt;allCurrentNovels&lt;/code&gt;, so that I could easily access the main novel in question for most of the app but also have that novel be visible to the user on the main page along with all the other novels in the contest. &lt;/p&gt;

&lt;p&gt;Basically: when using &lt;code&gt;rootReducer&lt;/code&gt;, each reducer going into it is responsible for its own piece of the store. &lt;code&gt;currentNovel&lt;/code&gt; is either set to &lt;code&gt;null&lt;/code&gt; or a user's novel. &lt;code&gt;allCurrentNovels&lt;/code&gt; includes everything. What this means practically is that every change to &lt;code&gt;currentNovel&lt;/code&gt; in its reducer must also find and change that novel in the &lt;code&gt;allCurrentNovels&lt;/code&gt; array, because the novel is also in there, and how it appears there influences how it looks in the app somewhere else. &lt;/p&gt;

&lt;p&gt;Here are a couple of excerpts from switch statements in each reducer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// /reducers/currentNovel.js&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="s1"&gt;UPDATE_NOVEL&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;novel&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ADD_BADGE&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="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="na"&gt;badges&lt;/span&gt;&lt;span class="p"&gt;:&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;badges&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;badge&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="c1"&gt;// /reducers/allCurrentNovels.js&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="s1"&gt;ADD_NOVEL&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="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="nx"&gt;novel&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="s1"&gt;ADD_BADGE&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;novel&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;novel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;badge&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;novel_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;novel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;badges&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;novel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;badges&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;badge&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
          &lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&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;novel&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;currentNovel&lt;/code&gt; reducer is only responsible for the key of &lt;code&gt;currentNovel&lt;/code&gt; in the Redux store, which is either null or an object, so it only has to return the payload of the action dispatched to it. The &lt;code&gt;allCurrentNovels&lt;/code&gt; reducer is responsible for an array of novels, so adding a &lt;code&gt;currentNovel&lt;/code&gt; needs to affect it too--but because it needs to hold all of the other novels too, the new state it returns spreads the old state into a new array which &lt;em&gt;also&lt;/em&gt; includes the new novel object. &lt;/p&gt;

&lt;p&gt;Similarly, with adding a badge to the &lt;code&gt;currentNovel&lt;/code&gt;, the &lt;code&gt;currentNovel&lt;/code&gt; reducer only has to return an object that spreads the rest of the pre-existing state into it, and then spreads the pre-existing badges state into the novel's badges array while also adding the new badge. The &lt;code&gt;allCurrentNovels&lt;/code&gt; reducer needs to deal with this action in a similar way, but it needs to find the right novel before modifying it. &lt;/p&gt;

&lt;p&gt;While my project set up might be a bit of a fringe case for this, how an action might need to go through multiple reducers is a good thing to keep in mind for a variety of different scenarios. Another one from my project that comes to mind is that when an action with the type &lt;code&gt;"CLEAR_CURRENT_USER"&lt;/code&gt; fires, it needs to be in the &lt;code&gt;currentUser&lt;/code&gt; reducer to set the &lt;code&gt;currentUser&lt;/code&gt; object to &lt;code&gt;null&lt;/code&gt;, but also in the &lt;code&gt;currentNovel&lt;/code&gt; reducer to set that object to null--and in whatever other reducers deal with pieces of the store that should be influenced by a user logging out. &lt;/p&gt;

&lt;p&gt;I hope this is helpful to anyone else struggling to get their head around multiple reducers like I was! &lt;/p&gt;

</description>
      <category>redux</category>
      <category>react</category>
      <category>codenewbie</category>
    </item>
  </channel>
</rss>
