<?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: Josh Tynjala</title>
    <description>The latest articles on Forem by Josh Tynjala (@joshtynjala).</description>
    <link>https://forem.com/joshtynjala</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%2F106604%2Fb803008c-b8d3-43d7-84bf-0642a3e3b687.png</url>
      <title>Forem: Josh Tynjala</title>
      <link>https://forem.com/joshtynjala</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/joshtynjala"/>
    <language>en</language>
    <item>
      <title>Upgrading a React and TypeScript app from react-scripts-ts to Create React App v2.1</title>
      <dc:creator>Josh Tynjala</dc:creator>
      <pubDate>Sun, 25 Nov 2018 18:03:52 +0000</pubDate>
      <link>https://forem.com/joshtynjala/upgrading-a-react-and-typescript-app-from-react-scripts-ts-to-create-react-app-v21-4akn</link>
      <guid>https://forem.com/joshtynjala/upgrading-a-react-and-typescript-app-from-react-scripts-ts-to-create-react-app-v21-4akn</guid>
      <description>&lt;p&gt;Recently, the React team updated &lt;a href="https://facebook.github.io/create-react-app/"&gt;&lt;strong&gt;Create React App&lt;/strong&gt;&lt;/a&gt; with &lt;a href="https://facebook.github.io/create-react-app/docs/adding-typescript"&gt;official support for &lt;strong&gt;TypeScript&lt;/strong&gt;&lt;/a&gt;. Create React App makes it easy to create web apps with React by keeping the details of the build process out of your way. No need to configure Webpack, Babel, and other tools because Create React App offers very high quality defaults. It’s not a boilerplate, though. You don’t even need to see the build dependencies and scripts, unless you want to, and upgrading to new versions is super easy. &lt;a href="http://www.typescriptlang.org/"&gt;TypeScript&lt;/a&gt; is a popular extension to JavaScript that adds compile-time type checking. The two combined, in my opinion, provides one of the best developer experiences you can get for modern frontend web development.&lt;/p&gt;

&lt;p&gt;Previously, to use TypeScript with Create React App, you had to create your project with the custom &lt;a href="https://www.npmjs.com/package/react-scripts-ts"&gt;&lt;em&gt;react-scripts-ts&lt;/em&gt;, created by Will Monk&lt;/a&gt;. Starting in version 2.1 of Create React App, TypeScript works out of the box. I have several projects that were started with &lt;em&gt;react-scripts-ts&lt;/em&gt;, and I recently decided to start upgrading them to Create React App v2.1. I thought I’d share the steps that I followed and my experiences with this process.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create a new baseline app
&lt;/h3&gt;

&lt;p&gt;To start, I decided to create a completely new app with Create React App. I figured that it would be a good idea to have a baseline app, where I could compare its files — like &lt;em&gt;package.json&lt;/em&gt; and &lt;em&gt;tsconfig.json&lt;/em&gt; — with the files from my existing projects. This new app would give me clues about what exactly I need to change to make the existing apps work properly with the new version of Create React App.&lt;/p&gt;

&lt;p&gt;To create a new app, I ran the following command in my terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;npx create-react-app my-new-app &lt;span class="nt"&gt;--typescript&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Update library dependencies
&lt;/h3&gt;

&lt;p&gt;In the existing project that I was upgrading, some of my dependencies hadn’t been updated in a little while, so the next step I decided on was to update the &lt;em&gt;react&lt;/em&gt;, &lt;em&gt;react-dom&lt;/em&gt;, &lt;em&gt;typescript&lt;/em&gt; dependencies, along with the type definitions, like &lt;em&gt;@types/react&lt;/em&gt;, &lt;em&gt;@types/react-dom&lt;/em&gt;, &lt;em&gt;@types/node&lt;/em&gt;, and &lt;em&gt;@types/jest&lt;/em&gt;. After updating the versions in &lt;em&gt;package.json&lt;/em&gt;, I ran &lt;code&gt;npm install&lt;/code&gt; to download them all.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I left &lt;em&gt;react-scripts-ts&lt;/em&gt; untouched for now.&lt;/strong&gt; The idea was to make sure that the existing project was using the exact same versions of the dependencies as the new baseline project, and it should still build properly with &lt;em&gt;react-scripts-ts&lt;/em&gt;. It’s best to ensure that I wouldn’t run into any issues with new versions of React or TypeScript that might confuse me as a worked through any issues caused by the differences between &lt;em&gt;react-scripts-ts&lt;/em&gt; and the official &lt;em&gt;react-scripts&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;I needed to make a couple of minor tweaks to so that the new version of the TypeScript compiler was happy, so it was a good thing that I took this extra step.&lt;/p&gt;

&lt;h3&gt;
  
  
  Switch from &lt;em&gt;react-scripts-ts&lt;/em&gt; to &lt;em&gt;react-scripts&lt;/em&gt;
&lt;/h3&gt;

&lt;p&gt;Once all of my library dependencies matched and the project would still build with &lt;em&gt;react-scripts-ts&lt;/em&gt;, it was time to switch to the official &lt;em&gt;react-scripts&lt;/em&gt;. Inside &lt;em&gt;package.json&lt;/em&gt;, I replaced the &lt;em&gt;react-scripts-ts&lt;/em&gt; dependency with &lt;em&gt;react-scripts&lt;/em&gt;. I copied the version number of &lt;em&gt;react-scripts&lt;/em&gt; from my new baseline project. Then, I needed to update the npm scripts inside &lt;em&gt;package.json&lt;/em&gt; to use &lt;em&gt;react-scripts&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"react-scripts start"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"react-scripts build"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"react-scripts test"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"eject"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"react-scripts eject"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;I replaced &lt;em&gt;react-scripts-ts&lt;/em&gt; with &lt;em&gt;react-scripts&lt;/em&gt; in each of the npm scripts above. Then, inside the &lt;strong&gt;build&lt;/strong&gt; script, I saw that my existing project included a &lt;code&gt;--env=jsdom&lt;/code&gt; option, but the new baseline project did not. I removed this option so that they matched.&lt;/p&gt;

&lt;h3&gt;
  
  
  Update configuration files
&lt;/h3&gt;

&lt;p&gt;Next, I compared &lt;em&gt;tsconfig.json&lt;/em&gt; in both projects. I could see some major differences, like outputting ES Modules instead of CommonJS, and preserving JSX so that Babel could handle it instead of letting the TypeScript compiler do everything. I decided that I would simply copy the contents of &lt;em&gt;tsconfig.json&lt;/em&gt; from the new baseline project to the existing project and then make a couple of small tweaks. I ended up setting the &lt;code&gt;lib&lt;/code&gt; compiler option to &lt;code&gt;["es2015", "dom"]&lt;/code&gt; so that I could access some newer JS APIs, and I set the &lt;code&gt;strict&lt;/code&gt; compiler option to &lt;code&gt;false&lt;/code&gt; because I haven’t updated my project to use strict mode yet. Otherwise, I used Create React App’s defaults, which all seem to be working well.&lt;/p&gt;

&lt;p&gt;Similarly, I compared &lt;em&gt;package.json&lt;/em&gt; in the two projects. I had already updated the dependencies and npm scripts, so the differences at this point were very minor. I saw that the new baseline project included two new fields that were not in the existing project, &lt;code&gt;eslintConfig&lt;/code&gt; and &lt;code&gt;browserslist&lt;/code&gt;. These defaults (which configure eslint and Babel) provided by Create React App seemed fine, so I copied them over.&lt;/p&gt;

&lt;p&gt;In theory, the migration process should be done at this point. If all goes well, you should be able to run &lt;code&gt;npm start&lt;/code&gt; to launch the development server, or you can run &lt;code&gt;npm run build&lt;/code&gt; to create a production build. For my project, I ran into a couple of issues that I needed to resolve, but nothing major.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fix compiler errors
&lt;/h3&gt;

&lt;p&gt;I use the &lt;a href="https://jamie.build/react-loadable.html"&gt;&lt;em&gt;react-loadable&lt;/em&gt;&lt;/a&gt; library to load parts of my app at runtime using code splitting. My imports in TypeScript originally looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;ReactLoadable&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-loadable&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;However, there seemed to be a problem with the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;AdminHomeScreen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ReactLoadable&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./AdminHomeScreen&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;LoadingScreen&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The TypeScript compiler gave me the following error:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Cannot invoke an expression whose type lacks a call signature. Type '{ default: Loadable; Map&amp;lt;Props, Exports extends { [key: string]: any; }&amp;gt;(options: OptionsWithMap&amp;lt;Props, Exports&amp;gt;): (ComponentClass&amp;lt;Props, any&amp;gt; &amp;amp; LoadableComponent) | (FunctionComponent&amp;lt;Props&amp;gt; &amp;amp; LoadableComponent); preloadAll(): Promise&amp;lt;...&amp;gt;; preloadReady(): Promise&amp;lt;...&amp;gt;; Capture: ComponentType&amp;lt;...&amp;gt;; }' has no compatible call signatures.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For some reason, I could not call the &lt;code&gt;ReactLoadable&lt;/code&gt; module as a function. It turned out that I needed to change the import to look like this instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;ReactLoadable&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-loadable&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In order to call a module like a function, you need to import the module’s &lt;em&gt;default export&lt;/em&gt;. I think this may have been related to switching from CommonJS modules to ES Modules in &lt;em&gt;tsconfig.json&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;After fixing this issue, I could successfully run &lt;code&gt;npm start&lt;/code&gt; to launch the development server, and I could open my app in a web browser and use it! However, I discovered one last issue that I needed to fix.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fix production build
&lt;/h3&gt;

&lt;p&gt;While &lt;code&gt;npm start&lt;/code&gt; worked for development, I got a strange error when I ran &lt;code&gt;npm run build&lt;/code&gt; to create a production build of my React app.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;module.js:550&lt;br&gt;&lt;br&gt;
throw err;&lt;br&gt;&lt;br&gt;
^&lt;/p&gt;

&lt;p&gt;Error: Cannot find module ‘webpack/lib/RequestShortener’&lt;br&gt;&lt;br&gt;
at Function.Module._resolveFilename (module.js:548:15)&lt;br&gt;&lt;br&gt;
at Function.Module._load (module.js:475:25)&lt;br&gt;&lt;br&gt;
at Module.require (module.js:597:17)&lt;br&gt;&lt;br&gt;
at require (internal/module.js:11:18)&lt;br&gt;&lt;br&gt;
at Object. (C:\Users\josht\Development\karaoke-butler\karaoke\client\node_modules\terser-webpack-plugin\dist\index.js:19:25)&lt;br&gt;&lt;br&gt;
at Module._compile (module.js:653:30)&lt;br&gt;&lt;br&gt;
at Object.Module._extensions..js (module.js:664:10)&lt;br&gt;&lt;br&gt;
at Module.load (module.js:566:32)&lt;br&gt;&lt;br&gt;
at tryModuleLoad (module.js:506:12)&lt;br&gt;&lt;br&gt;
at Function.Module._load (module.js:498:3)&lt;br&gt;&lt;br&gt;
npm ERR! code ELIFECYCLE&lt;br&gt;&lt;br&gt;
npm ERR! errno 1&lt;br&gt;&lt;br&gt;
npm ERR! &lt;a href="mailto:react-client@1.0.0"&gt;react-client@1.0.0&lt;/a&gt; build: &lt;code&gt;react-scripts build&lt;/code&gt;&lt;br&gt;&lt;br&gt;
npm ERR! Exit status 1&lt;br&gt;&lt;br&gt;
npm ERR!&lt;br&gt;&lt;br&gt;
npm ERR! Failed at the &lt;a href="mailto:react-client@1.0.0"&gt;react-client@1.0.0&lt;/a&gt; build script.&lt;br&gt;&lt;br&gt;
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.&lt;/p&gt;

&lt;p&gt;npm ERR! A complete log of this run can be found in:&lt;br&gt;&lt;br&gt;
npm ERR! C:\Users\josht\AppData\Roaming\npm-cache_logs\2018-11-25T01_57_14_654Z-debug.log&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Unfortunately, a quick Google search did not find anyone having the same issue with Create React App. However, I saw some folks using other frameworks, like Vue or Angular, were getting similar errors. Eventually, I found someone who discovered that it was some kind of conflict inside the notorious &lt;em&gt;package-lock.json&lt;/em&gt;. Ever since npm started using this file, it’s been a terrible source of problems, in my experience.&lt;/p&gt;

&lt;p&gt;Anyway, I deleted &lt;em&gt;package-lock.json&lt;/em&gt; and the &lt;em&gt;node_modules&lt;/em&gt; folder. Then, I ran &lt;code&gt;npm install&lt;/code&gt; to install all of my dependencies from scratch. That did the trick because &lt;code&gt;npm run build&lt;/code&gt; started working correctly.&lt;/p&gt;

&lt;p&gt;I don’t really know why &lt;em&gt;package-lock.json&lt;/em&gt; always seems to cause problems like this, but I find that deleting this file and the entire &lt;em&gt;node_modules&lt;/em&gt; folder frequently fixes issues that I run into. I don’t know if I’m doing something wrong, or if it’s a bug in npm.&lt;/p&gt;

&lt;h3&gt;
  
  
  My project is upgraded
&lt;/h3&gt;

&lt;p&gt;Updating dependencies in some of my web projects can be daunting sometimes. For some of them, I usually need to set a full day aside to fix everything. With that in mind, I was expecting to hit a few more snags in the process of upgrading from &lt;em&gt;react-scripts-ts&lt;/em&gt; to the official version of Create React App. However, the changes required in my app were relatively minor. The process felt very smooth, and I’m happy that I’ll be able to easily update to new versions of Create React App in the future as the React team continues to innovate.&lt;/p&gt;

</description>
      <category>react</category>
      <category>typescript</category>
      <category>createreactapp</category>
    </item>
    <item>
      <title>Enabling the back button to cancel confirmation dialogs in React apps</title>
      <dc:creator>Josh Tynjala</dc:creator>
      <pubDate>Sun, 21 Oct 2018 23:46:00 +0000</pubDate>
      <link>https://forem.com/joshtynjala/enabling-the-back-button-to-cancel-confirmation-dialogs-in-react-apps-1ng9</link>
      <guid>https://forem.com/joshtynjala/enabling-the-back-button-to-cancel-confirmation-dialogs-in-react-apps-1ng9</guid>
      <description>&lt;p&gt;Users expect that dialogs, action sheets, alerts, drawers, and other pop-ups will close when the back button is pressed. This is especially true on Android (where the back button is globally available in all apps), but this behavior makes sense for any apps running in a web browser too — on both mobile and desktop. Usually, when a dialog is open, the back button should act the same as if the user had chosen the “Cancel” option. The worst thing that could happen (but it’s surprisingly common, unfortunately) is that the dialog will close and the browser will navigate away from the page that opened the dialog!&lt;/p&gt;

&lt;p&gt;In a React app, we can use the browser’s history (exposed by our routing library of choice) to ensure that pressing the back button when a dialog is open closes the dialog and keeps the app on the same screen that opened the dialog. As a bonus, if the user navigates forward again, we can restore the dialog to its previous state very easily. Let’s take a look.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://codesandbox.io/s/n74l8jpv6p"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BNxsahKs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/lmv22wfpt4y84oonqybr.png" alt="" width="800" height="631"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For convenience, I created &lt;a href="https://codesandbox.io/s/n74l8jpv6p"&gt;an example on CodeSandbox&lt;/a&gt; to demonstrate the final result. In this example, I’ve added &lt;a href="https://reach.tech/router/"&gt;Reach Router&lt;/a&gt; and &lt;a href="https://material-ui.com/"&gt;Material UI&lt;/a&gt; as dependencies to provide the routing/navigation and some UI controls. The concept that I’m demonstrating here should work just as well with &lt;a href="https://reacttraining.com/react-router/web/"&gt;React Router&lt;/a&gt; and other UI control libraries without too many changes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://codesandbox.io/s/n74l8jpv6p"&gt;&lt;br&gt;&lt;br&gt;
 &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1rJFNpIB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://codesandbox.io/static/img/play-codesandbox.svg" alt="Edit React dialogs with back button" width="201" height="42"&gt;&lt;br&gt;&lt;br&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In our example app, we have a list of users. When you click/tap a user’s name, a dialog will pop up with some actions that can be performed. Or you can press cancel to close the dialog. Our goal is to ensure that the browser’s back button will close the dialog without navigating away from the list of all users — the same as if the cancel option were chosen with a click/tap.&lt;/p&gt;

&lt;p&gt;We can pass the user to dialog using the browser history’s state object. We’ll push a new state onto the stack (without changing the URL), and the dialog will check the current state to know when it should open or close.&lt;/p&gt;

&lt;p&gt;Let’s start by rendering the list of users using some components from Material UI:&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;// inside render()&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&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;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ListItem&lt;/span&gt;
        &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&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;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;button&lt;/span&gt;
        &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;selectUser&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="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="nc"&gt;ListItemIcon&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="nc"&gt;Person&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="nc"&gt;ListItemIcon&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="nc"&gt;ListItemText&lt;/span&gt; &lt;span class="na"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&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;name&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="nc"&gt;ListItem&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have an array of users that we map to &lt;code&gt;ListItem&lt;/code&gt; components. When a list item is clicked/tapped, we call a function called &lt;code&gt;selectUser()&lt;/code&gt; that will manipulate the browser’s history.&lt;/p&gt;

&lt;p&gt;For reference, a user in the array looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Franklin Nelson&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In &lt;code&gt;selectUser()&lt;/code&gt;, we use the &lt;code&gt;navigate()&lt;/code&gt; function provided to our component by Reach Router to add the user to the history stack.&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="nf"&gt;selectUser&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="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;newState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;selectedUser&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="k"&gt;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="nf"&gt;navigate&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;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pathname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;newState&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;Notice that we use &lt;code&gt;this.props.location.pathname&lt;/code&gt; (also provided by Reach Router) to ensure that the URL does not change. We’re staying at the same location, but a new history entry is added that has a different state. This means that when we go back, we’ll stay on the same page.&lt;/p&gt;

&lt;p&gt;Next, let’s see how our &lt;code&gt;UserActionsDialog&lt;/code&gt; component is rendered.&lt;/p&gt;

&lt;p&gt;As a first step, we should check if the current history state has a selected user. If no user is selected, we’ll simply return &lt;code&gt;null&lt;/code&gt; because we don’t want to render the dialog:&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;// inside UserActionsDialog's render()&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;state&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;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;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;state&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;selectedUser&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&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;selectedUser&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&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="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If a user is selected, we should render our dialog. It might look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// continued from above&lt;/span&gt;
&lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Dialog&lt;/span&gt; &lt;span class="na"&gt;open&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="nc"&gt;DialogTitle&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&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;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;DialogTitle&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="nc"&gt;List&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="nc"&gt;ListItem&lt;/span&gt;
        &lt;span class="na"&gt;button&lt;/span&gt;
        &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Option 1 selected for &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&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;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ListItemText&lt;/span&gt; &lt;span class="na"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Option 1"&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="nc"&gt;ListItem&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="nc"&gt;ListItem&lt;/span&gt;
        &lt;span class="na"&gt;button&lt;/span&gt;
        &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Option 2 selected for &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&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;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ListItemText&lt;/span&gt; &lt;span class="na"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Option 2"&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="nc"&gt;ListItem&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="nc"&gt;ListItem&lt;/span&gt;
        &lt;span class="na"&gt;button&lt;/span&gt;
        &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cancelItem_onClick&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="nc"&gt;ListItemText&lt;/span&gt; &lt;span class="na"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Cancel"&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="nc"&gt;ListItem&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="nc"&gt;List&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="nc"&gt;Dialog&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that we have access to the full user object selected in the parent component. We’re accessing its &lt;code&gt;name&lt;/code&gt; field here.&lt;/p&gt;

&lt;p&gt;For simplicity, the first two actions simply call &lt;code&gt;alert()&lt;/code&gt;. The third option is named “Cancel” and it should close the dialog. Clicking this item calls &lt;code&gt;cancelItem_onClick()&lt;/code&gt;, which appears below:&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="nx"&gt;cancelItem_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="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;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;back&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;When we call &lt;code&gt;window.history.back()&lt;/code&gt;, the selected user is removed from the history stack and our &lt;code&gt;UserActionsDialog&lt;/code&gt; component will re-render and return &lt;code&gt;null&lt;/code&gt;. That will cause the dialog to be hidden.&lt;/p&gt;

&lt;p&gt;Notice that we don’t do anything to cancel except to navigate back. With that in mind, we don’t need to do to do anything more to enable the back button. It just works! In fact, after we navigate back, the browser’s forward button will work too, and the dialog will re-open… including the same selected user.&lt;/p&gt;

&lt;p&gt;Back in the parent component that contains our &lt;code&gt;List&lt;/code&gt; of users, let’s see how to add our &lt;code&gt;UserActionsDialog&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// this goes after the list of all users&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Router&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="nc"&gt;UserActionsDialog&lt;/span&gt; &lt;span class="na"&gt;path&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;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pathname&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="nc"&gt;Router&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that we place &lt;code&gt;UserActionsDialog&lt;/code&gt; inside a &lt;code&gt;Router&lt;/code&gt; component. This ensures that the &lt;code&gt;location&lt;/code&gt; prop is passed into the dialog so that we can find the selected user. You can nest routers, so it’s fine if there’s another router further up the component tree.&lt;/p&gt;

&lt;p&gt;As I mentioned before, the route’s &lt;code&gt;path&lt;/code&gt; is the same as its parent. We shouldn’t need to change the URL when selecting a user because that wouldn’t make much sense if the user wanted to share the page that contains the dialog. Since the current state object stores the user, that’s what we use to determine if the dialog should be open or closed.&lt;/p&gt;

&lt;p&gt;Even when using some kind of router, many web apps don’t account for all cases where the back button should behave a certain way. Opening dialogs to select an action is a common place where the user might press the back button to cancel, and you don’t want your app to navigate to a different page unexpectedly. On Android, this is especially important because the global back button is such a core part of the user experience. However, it’s just as useful in a web browser on any platform, including on desktop.&lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>frontend</category>
      <category>router</category>
    </item>
  </channel>
</rss>
