<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[JavaScript Ramblings]]></title><description><![CDATA[Thoughts on JavaScript and React.]]></description><link>https://jsramblings.com/</link><image><url>https://jsramblings.com/favicon.png</url><title>JavaScript Ramblings</title><link>https://jsramblings.com/</link></image><generator>Ghost 5.60</generator><lastBuildDate>Thu, 09 Apr 2026 18:04:16 GMT</lastBuildDate><atom:link href="https://jsramblings.com/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Best practices for working with API keys in the frontend]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>Ever wondered if it&#x2019;s ok to have your API key bundled with the frontend, public for anyone who uses your app to see?<br>
You know you should never put secrets in the UI. But then you see people bundling their keys with the UI all the time (Firebase</p>]]></description><link>https://jsramblings.com/best-practices-for-working-with-api-keys-in-the-frontend/</link><guid isPermaLink="false">64ecfadfa81cfd3240134834</guid><dc:creator><![CDATA[Corina]]></dc:creator><pubDate>Tue, 24 Jan 2023 08:07:26 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>Ever wondered if it&#x2019;s ok to have your API key bundled with the frontend, public for anyone who uses your app to see?<br>
You know you should never put secrets in the UI. But then you see people bundling their keys with the UI all the time (Firebase API key, anyone? &#x1F600;)</p>
<p>So how can you tell what to do in your case?<br>
This article aims to shed some light on how you can decide for yourself.</p>
<h2 id="be-aware-that-there-are-no-secrets-in-the-frontend">Be aware that there are no secrets in the frontend</h2>
<p>Before moving on to more specific advice, it&#x2019;s good to get one thing clear first: there are no secrets in the UI.</p>
<p>Your frontend, after being compiled, is really just a bunch of HTML, Javascript and CSS files.<br>
And if there is any key that the UI needs, it&#x2019;s usually just hardcoded as a variable in the Javascript code.<br>
This means that someone can inspect all the global variables your have in memory and track down the key.</p>
<p>But they don&#x2019;t usually need to go such lengths - the keys are usually used to call APIs, so it&#x2019;s enough to open the Network tab to see what request parameters or request headers are sent and figure out the keys!</p>
<h2 id="consider-whether-your-api-keys-can-be-public-or-not">Consider whether your API keys can be public or not</h2>
<p>In general, you never want to share your API keys. This means you should only use your keys from the backend, where they are not accesible publicly.</p>
<p>However, there are some exceptions to this.</p>
<p>Some keys don&#x2019;t give access to any resources, but instead act as an &#x201C;ID&#x201D; that associates you or your application to a certain user account. A good example of this is the Firebase API key - the docs explicitly say it&#x2019;s ok to include this key in your code:</p>
<blockquote>
<p>Usually, you need to fastidiously guard API keys (for example, by using a vault service or setting the keys as environment variables); however, API keys for Firebase services are ok to include in code or checked-in config files. <a href="https://firebase.google.com/docs/projects/api-keys?ref=jsramblings.com#general-info">source</a></p>
</blockquote>
<p>Another exception could be for keys that only give access to public resources. Even if there is a risk that the key is compromised, the risk is very low - if anyone can get their own key for free, why would they steal yours? A good example of this is the Movie Database API - it&#x2019;s not ideal to have the key public, but the <a href="https://www.themoviedb.org/talk/505e1089760ee32de40000cf?ref=jsramblings.com">risk is also very low</a>.</p>
<p>Lastly, it could be ok to have your key in the UI if you setup some extra guards in place. Two common strategies are to only limit the usage of the key to <a href="https://stackoverflow.com/a/40962187?ref=jsramblings.com">requests coming from a certain domain</a> and to <a href="https://firebase.google.com/docs/projects/api-keys?ref=jsramblings.com#apply-restrictions-password-based-auth">setup restrictive quotas</a> to prevent brute force attacks.</p>
<h2 id="don%E2%80%99t-commit-your-secrets-to-git">Don&#x2019;t commit your secrets to git</h2>
<p>If you&#x2019;ve decided it&#x2019;s ok to use your key in the frontend, don&#x2019;t just include it in your code! Instead, extract it as a build-time environment variable.</p>
<p>This will allow you to configure the key based on different environments (staging/production etc.) and also make it a bit harder for someone to steal.</p>
<p>To achieve this, you&#x2019;d usually have a file containing the app configuration (usually called <code>.env</code>), that you don&#x2019;t commit to git (by ignoring it in your <code>.gitignore</code> file) and whose values you can access in your app as Node environment variables - <code>process.env.SOME_API_KEY</code>.</p>
<p>If you&#x2019;re using Webpack, you can setup <a href="https://www.npmjs.com/package/dotenv?ref=jsramblings.com">dotenv</a> and the <a href="https://www.npmjs.com/package/dotenv-webpack?ref=jsramblings.com">dotenv-webpack</a> plugin to get this to work. If you&#x2019;re using CRA, you get it <a href="https://create-react-app.dev/docs/adding-custom-environment-variables/?ref=jsramblings.com">out of the box</a>.</p>
<h2 id="conclusion">Conclusion</h2>
<p>I hope you found this overview useful. Do share your thoughts in the comments below if you have any feedback or questions!</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Create a React and Typescript app with Webpack]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>In a <a href="https://jsramblings.com/creating-a-react-app-with-webpack/">previous article</a>, I went over how you can setup a simple React app with just Webpack and Babel. But what if you also want to enable Typescript for your project?</p>
<p>This article goes over how to add Typescript support to a simple React + Webpack project.</p>
<h3 id="step-1-install-typescript-and-type-definitions">Step 1: Install</h3>]]></description><link>https://jsramblings.com/create-a-react-and-typescript-app-with-webpack/</link><guid isPermaLink="false">64ecfadfa81cfd3240134833</guid><dc:creator><![CDATA[Corina]]></dc:creator><pubDate>Thu, 19 Jan 2023 07:36:44 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>In a <a href="https://jsramblings.com/creating-a-react-app-with-webpack/">previous article</a>, I went over how you can setup a simple React app with just Webpack and Babel. But what if you also want to enable Typescript for your project?</p>
<p>This article goes over how to add Typescript support to a simple React + Webpack project.</p>
<h3 id="step-1-install-typescript-and-type-definitions">Step 1: Install Typescript and type definitions</h3>
<p>The first step is to just <a href="https://www.typescriptlang.org/download?ref=jsramblings.com">install Typescript</a>:</p>
<pre><code>npm install typescript --save-dev
</code></pre>
<p>Then, it helps to install the type definitions for all the libraries you already use. Usually, this would be:</p>
<pre><code>npm install @types/node @types/react @types/react-dom @types/jest --save-dev
</code></pre>
<h3 id="step-2-configure-webpack">Step 2: Configure Webpack</h3>
<p>For Webpack to be able to process Typescript files, we will first need to install a custom loader.</p>
<p>There are several available, but we&#x2019;ll use <a href="https://github.com/TypeStrong/ts-loader?ref=jsramblings.com"><code>ts-loader</code></a> for our setup:</p>
<pre><code>npm install ts-loader --save-dev
</code></pre>
<p>Next, we need to tell Webpack to process TS files as well. For this, we can update the <code>webpack.config.js</code> file to also support <code>ts</code> and <code>tsx</code> extensions:</p>
<pre><code>// webpack.config.js

{
  // ...,
  module: {
    rules: [
      // `js` and `jsx` files are parsed using `babel`
      {
        test: /\.(js|jsx)$/, 
        exclude: /node_modules/,
        use: [&quot;babel-loader&quot;],
      },
		// `ts` and `tsx` files are parsed using `ts-loader`
      { 
        test: /\.(ts|tsx)$/, 
        loader: &quot;ts-loader&quot; 
      }
    ],
  },
  resolve: {
    extensions: [&quot;*&quot;, &quot;.js&quot;, &quot;.jsx&quot;, &quot;.ts&quot;, &quot;.tsx&quot;],    
  },
}
</code></pre>
<h3 id="step-3-configure-typescript">Step 3: Configure Typescript</h3>
<p>The Typescript compiler supports several options for how to treat the code, which are defined in a <code>tsconfig.json</code> file in the root of the project.</p>
<p>Let&#x2019;s create one for this project as well:</p>
<pre><code>// tsconfig.json
{
  &quot;compilerOptions&quot;: {  
    &quot;jsx&quot;: &quot;react-jsx&quot;,
  }
}
</code></pre>
<p>For the purpose of this tutorial, I just added the minimal settings that are required for the React + TS + Webpack integration, but you can learn more about all the options available in the <a href="https://www.typescriptlang.org/docs/handbook/tsconfig-json.html?ref=jsramblings.com">official documentation</a>.</p>
<h3 id="step-4-start-using-typescript">Step 4: Start using Typescript</h3>
<p>If you already have some Javascript files in your project, now is a good time to change their extensions from <code>js</code> to <code>ts</code> and from <code>jsx</code> to <code>tsx</code>!</p>
<p>Then, restart the app and you should see everything still working &#x2728;</p>
<p>Check out the completed repository at <a href="https://github.com/reactpractice-dev/basic-react-ts-boilerplate?ref=jsramblings.com">https://github.com/reactpractice-dev/basic-react-ts-boilerplate</a>.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Dockerizing a React app]]></title><description><![CDATA[<p>So you have a React app. And you want to serve it through Docker.</p><p>Let&apos;s do that!</p><p>At the end of this tutorial, you&apos;ll have a Docker container running your app that you can deploy as you see fit &#x1F44C;</p><p>We&apos;re going to start</p>]]></description><link>https://jsramblings.com/dockerizing-a-react-app/</link><guid isPermaLink="false">64ecfadfa81cfd3240134832</guid><dc:creator><![CDATA[Corina]]></dc:creator><pubDate>Sun, 19 Jun 2022 12:45:52 GMT</pubDate><content:encoded><![CDATA[<p>So you have a React app. And you want to serve it through Docker.</p><p>Let&apos;s do that!</p><p>At the end of this tutorial, you&apos;ll have a Docker container running your app that you can deploy as you see fit &#x1F44C;</p><p>We&apos;re going to start from an existing app - a barebones app, using just Webpack and React. Find the <a href="https://github.com/jsramblings/react-webpack-setup?ref=jsramblings.com">starter code on Github</a> or <a href="https://jsramblings.com/creating-a-react-app-with-webpack/">follow a step by step tutorial to setup the app</a>.</p><h2 id="step-1-building-a-docker-image">Step 1: Building a Docker image</h2><p>To build a Docker image, we want to copy all the source files inside the container, build the project (also inside the container) and then serve the <code>build</code> folder.</p><p>Let&apos;s start by ignoring the files that we never want to copy to the docker image. For this, we&apos;ll create a <code>.dockerignore</code> file in the root of the project:</p><pre><code>// .dockerignore
node_modules
build

</code></pre><p>Next, let&apos;s define our Docker container by creating a <code>Dockerfile</code> in the root of the project:</p><pre><code>// Dockerfile

# ==== CONFIGURE =====
# Use a Node 16 base image
FROM node:16-alpine 
# Set the working directory to /app inside the container
WORKDIR /app
# Copy app files
COPY . .
# ==== BUILD =====
# Install dependencies (npm ci makes sure the exact versions in the lockfile gets installed)
RUN npm ci 
# Build the app
RUN npm run build
# ==== RUN =======
# Set the env to &quot;production&quot;
ENV NODE_ENV production
# Expose the port on which the app will be running (3000 is the default that `serve` uses)
EXPOSE 3000
# Start the app
CMD [ &quot;npx&quot;, &quot;serve&quot;, &quot;build&quot; ]</code></pre><p>Notes</p><ul><li>Using `alpine` flavour of an image (e.g. `node:16-alpine` instead of `node:16`) will give you a smaller image size</li><li>Check what the latest LTS Node version is and use that Docker image. At the time of writing of this article, it was Node 16</li><li>For simplicity, we&apos;re just serving the app with <code>npx serve</code> for now. Step 2 will add nginx, so stay tuned!</li></ul><p>Ok, let&apos;s build the Docker image and run it, to make sure everything works.</p><pre><code># Build the Docker image for the current folder 
# and tag it with `dockerized-react`
docker build . -t dockerized-react

# Check the image was created
docker images | grep dockerized-react

# Run the image in detached mode 
# and map port 3000 inside the container with 3000 on current host
docker run -p 3000:3000 -d dockerized-react</code></pre><p>Now open the app at <code>http://localhost:3000</code> - you should see &quot;Hello from React&quot; &#x1F600;</p><figure class="kg-card kg-image-card"><img src="https://jsramblings.com/content/images/2022/06/hello-from-react.png" class="kg-image" alt loading="lazy" width="1164" height="480" srcset="https://jsramblings.com/content/images/size/w600/2022/06/hello-from-react.png 600w, https://jsramblings.com/content/images/size/w1000/2022/06/hello-from-react.png 1000w, https://jsramblings.com/content/images/2022/06/hello-from-react.png 1164w" sizes="(min-width: 720px) 720px"></figure><h2 id="step-2serve-the-app-through-nginx">Step 2 - Serve the app through nginx</h2><p>While serving the app with <code>serve</code> is ok for small apps, it&apos;s common to just use <code>nginx</code> to serve the static files, so let&apos;s set that up as well. </p><p>Also, while we&apos;re at it, we&apos;ll separate the &quot;build&quot; and &quot;run&quot; steps of creating the Docker image by taking advantage of Docker image layers. This allows us to run the build in a <code>node</code> image and server the app using an <code>nginx</code> image.</p><p>To begin, let&apos;s create an <code>nginx.conf</code> file in the root of the project:</p><pre><code>// nginx.conf

server {
  listen 80;

  location / {
    root /usr/share/nginx/html/;
    include /etc/nginx/mime.types;
    try_files $uri $uri/ /index.html;
  }
}</code></pre><p>Next, let&apos;s update the <code>Dockerfile</code> to with separate layers and serving the app with <code>nginx</code>:</p><pre><code>FROM node:16-alpine as builder
# Set the working directory to /app inside the container
WORKDIR /app
# Copy app files
COPY . .
# Install dependencies (npm ci makes sure the exact versions in the lockfile gets installed)
RUN npm ci 
# Build the app
RUN npm run build

# Bundle static assets with nginx
FROM nginx:1.21.0-alpine as production
ENV NODE_ENV production
# Copy built assets from `builder` image
COPY --from=builder /app/build /usr/share/nginx/html
# Add your nginx.conf
COPY nginx.conf /etc/nginx/conf.d/default.conf
# Expose port
EXPOSE 80
# Start nginx
CMD [&quot;nginx&quot;, &quot;-g&quot;, &quot;daemon off;&quot;]</code></pre><p>Now, the final Docker image will just contain the <code>build</code> folder and nothing else - the project files were only used by to build the project in the <code>builder</code> layer, which then gets thrown away - it&apos;s just an intermmediary step.</p><p>Cool, now let&apos;s check that this works by building and running the Docker image.</p><p>First, let&apos;s stop the previously started container:</p><pre><code># See all running containers
docker ps

# Stop the previous container
# Copy &quot;Name&quot; or &quot;Container ID&quot; of your container
# and pass it into docker stop
docker stop &lt;container name|id&gt;</code></pre><p>Next, let&apos;s rebuild the image and run it:</p><pre><code>docker build . -t dockerized-react

# Notice we&apos;re now mapping port 80 inside the container 
# to port 3000 on the host machine!
docker run -p 3000:80 -d dockerized-react</code></pre><p>Navigate to <code>http://localhost:3000</code> to see it&apos;s still working!</p><hr><p>That&apos;s it! Let me know in the comments below if this worked for you &#x1F4AA;</p><p>You can checkout the repository for this tutorial at: <a href="https://github.com/jsramblings/dockerize-react?ref=jsramblings.com">https://github.com/jsramblings/dockerize-react</a></p>]]></content:encoded></item><item><title><![CDATA[Creating a React app with Webpack]]></title><description><![CDATA[Simple step by step walkthrough of setting up a React app with Webpack]]></description><link>https://jsramblings.com/creating-a-react-app-with-webpack/</link><guid isPermaLink="false">64ecfadfa81cfd3240134831</guid><dc:creator><![CDATA[Corina]]></dc:creator><pubDate>Mon, 06 Jun 2022 14:29:09 GMT</pubDate><content:encoded><![CDATA[<p>Sometimes you want to get started with React but don&apos;t want all the bloat of <code>create-react-app</code> or some other boilerplate. Here&apos;s a step by step walkthrough of how to set to setup React with just Webpack!</p><h3 id="what-the-project-will-look-like-when-its-done">What the project will look like when it&apos;s done</h3><p>This guide will follow the conventions already established by <code>create-react-app</code> - e.g. build folder is called <code>build</code>, static assets are under <code>public</code>, etc.</p><p>At the end, this is the folder structure we&apos;ll have:</p><pre><code>- public
   - index.html
- src
   - App.jsx
webpack.config.js
.babelrc
package.json</code></pre><p>We&apos;ll go step by step and check that everything works after every stage:</p><ol><li>Basic scaffolding: create project folder and serve plain HTML</li><li>Add Webpack and bundle a simple JS file</li><li>Add Babel for ES6 support</li><li>Add React</li></ol><p>Without further ado, let&apos;s get started!</p><h3 id="step-1-base-scaffolding">Step 1: Base scaffolding</h3><p>First step is to create the project folder and add a plain HTML file.</p><p>To create the project and initialize the <code>package.json</code>, run these commands:</p><pre><code class="language-bash">mkdir react-webpack
cd react-webpack
npm init -y</code></pre><p>Then, add the main index <code>html</code> file - it usually sits in a <code>public</code> directory, so let&apos;s do that:</p><pre><code class="language-bash">mkdir public
touch public/index.html</code></pre><div class="kg-card kg-callout-card kg-callout-card-grey"><div class="kg-callout-emoji">&#x1F4A1;</div><div class="kg-callout-text">Tip: You can open the project in VSCode by typing <code>code .</code> in the project folder and manually create the <code>public</code> folder from there</div></div><p>The <code>index.html</code> will just contain the base HTML5 boilerplate:</p><pre><code class="language-HTML">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
&lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot;&gt;
    &lt;meta http-equiv=&quot;X-UA-Compatible&quot; content=&quot;IE=edge&quot;&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;
    &lt;title&gt;React + Webpack&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;h1&gt;Hello React + Webpack!&lt;/h1&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><div class="kg-card kg-callout-card kg-callout-card-grey"><div class="kg-callout-emoji">&#x1F4A1;</div><div class="kg-callout-text">Tip: In VSCode, if you type `html:5` and hit tab, VSCode will create the `index.html` contents for you!</div></div><p>Now let&apos;s check it runs so far by just serving the HTML we just created with npm <a href="https://www.npmjs.com/package/serve?ref=jsramblings.com">serve</a>.</p><pre><code class="language-bash">npx serve public</code></pre><p>And it does! If you navigate to <a href="http://localhost:3000/?ref=jsramblings.com">http://localhost:3000</a>, you should see the hello message we just added:</p><figure class="kg-card kg-image-card"><img src="https://jsramblings.com/content/images/2022/06/image.png" class="kg-image" alt loading="lazy" width="842" height="258" srcset="https://jsramblings.com/content/images/size/w600/2022/06/image.png 600w, https://jsramblings.com/content/images/2022/06/image.png 842w" sizes="(min-width: 720px) 720px"></figure><div class="kg-card kg-callout-card kg-callout-card-grey"><div class="kg-callout-emoji">&#x1F4A1;</div><div class="kg-callout-text">Tip: If <code>npx serve public</code> fails for you with <code>Must use import to load ES Module</code> error, check your Node version and <a href="https://stackoverflow.com/a/72158539?ref=jsramblings.com">make sure you&apos;re using at least Node 16</a> (latest LTS).</div></div><h3 id="step-2-adding-webpack">Step 2: Adding Webpack</h3><p>For this section, it&apos;s best to just follow the latest <a href="https://webpack.js.org/guides/getting-started/?ref=jsramblings.com#basic-setup">official Webpack docs</a>.</p><p>First, install Webpack:</p><pre><code class="language-bash">npm install webpack webpack-cli --save-dev</code></pre><p>Next, let&apos;s create a simple JavaScript file that we can configure Webpack to bundle:</p><pre><code class="language-bash">mkdir src
touch src/index.js</code></pre><p>We can just create a div with a hello message and add it to the document:</p><pre><code class="language-js">// index.js

const helloDiv = document.createElement(&quot;div&quot;);
helloDiv.innerHTML = &quot;Hello from Javascript!&quot;;
document.body.append(helloDiv);</code></pre><p>The, we need to configure Webpack by creating a <code>webpack.config.js</code> file in the root of the project:</p><pre><code class="language-js">// webpack.config.js

const path = require(&quot;path&quot;);

module.exports = {
  entry: &quot;./src/index.js&quot;,
  output: {
    filename: &quot;main.js&quot;,
    path: path.resolve(__dirname, &quot;build&quot;),
  },
};
</code></pre><p>Finally, in <code>package.json</code>, add a new &quot;build&quot; script:</p><pre><code class="language-json">// ...
&quot;scripts&quot;: {
    &quot;build&quot;: &quot;webpack&quot;
},</code></pre><p>Now, let&apos;s try it out! After running <code>npm run build</code>, you should see a new folder was created, called <code>build</code>, with a <code>main.js</code> file in it! &#x1F4AA;</p><div class="kg-card kg-callout-card kg-callout-card-grey"><div class="kg-callout-emoji">&#x1F4A1;</div><div class="kg-callout-text">Tip: Add the <code>build</code> folder to <code>.gitignore</code> to not commit it by accident. And if you haven&apos;t already, make sure to also ignore the <code>node_modules</code> folder.</div></div><p>Next, we need to move the static assets to the bundle.</p><p>More specifically, we want to also include the <code>index.html</code> file in the <code>build</code> folder.</p><p>Easiest way to do this is with the <a href="https://webpack.js.org/plugins/html-webpack-plugin/?ref=jsramblings.com">HtmlWebpackPlugin</a>.</p><pre><code class="language-bash">npm install html-webpack-plugin --save-dev</code></pre><p>And update the <code>webpack.config.js</code>file:</p><pre><code class="language-js">const path = require(&quot;path&quot;);
const HtmlWebpackPlugin = require(&quot;html-webpack-plugin&quot;);

module.exports = {
  entry: &quot;./src/index.js&quot;,
  output: {
    filename: &quot;main.js&quot;,
    path: path.resolve(__dirname, &quot;build&quot;),
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: path.join(__dirname, &quot;public&quot;, &quot;index.html&quot;),
    }),
  ],
};
</code></pre><p>This will copy the file under <code>public/index.html</code>, copy it to the <code>build</code> folder and inject a link to the bundled JS file (<code>main.js</code>).</p><p>Let&apos;s try it out!</p><pre><code class="language-bash">npm run build
npx serve build</code></pre><p>And it works! You should now also see the message &quot;Hello from Javascript&quot; :D</p><figure class="kg-card kg-image-card"><img src="https://jsramblings.com/content/images/2022/06/image-1.png" class="kg-image" alt loading="lazy" width="842" height="296" srcset="https://jsramblings.com/content/images/size/w600/2022/06/image-1.png 600w, https://jsramblings.com/content/images/2022/06/image-1.png 842w" sizes="(min-width: 720px) 720px"></figure><h3 id="adding-webpack-dev-server">Adding Webpack dev server</h3><p>So far, it was ok to just use <code>npx serve</code> to check our app works, but in real life, it&apos;s easier to just use the <code>webpack-dev-server</code>, so let&apos;s add that as well.</p><pre><code class="language-bash">npm install --save-dev webpack-dev-server</code></pre><p>Then, configure it in the Webpack config:</p><pre><code class="language-js">{
  // ...,
  devServer: {
    static: {
      directory: path.join(__dirname, &quot;build&quot;),
    },
    port: 3000,
  }
}</code></pre><p>... and then add <code>npm run start</code> script to package json; and while we&apos;re there, pass in the right &quot;mode&quot;:</p><pre><code class="language-json">{
  // ...,
  &quot;scripts&quot;: {
    &quot;build&quot;: &quot;webpack --mode production&quot;,
    &quot;start&quot;: &quot;webpack serve --mode development&quot;
  }
}</code></pre><p>Finally, check that it works: run <code>npm run start</code>, &#xA0;open <a href="http://localhost:3000/?ref=jsramblings.com">http://localhost:3000</a> and check the app still works as before.</p><h3 id="step-3-adding-babel">Step 3: Adding Babel</h3><p>This is useful for allowing us to use all ES6 features and having them transpiled down to JS versions that all browsers can understand. (If you want to learn more about what Babel <code>preset-env</code> is and why it&apos;s useful, <a href="https://blog.jakoblind.no/babel-preset-env/?ref=jsramblings.com">this article</a> by Jacob Lind is a great overview).</p><p>First, let&apos;s install the required packages:</p><pre><code class="language-bash">npm i @babel/core @babel/preset-env babel-loader --save-dev</code></pre><p></p><p>Next, update the Webpack config to tell it to pass the files through Babel when bundling:</p><pre><code class="language-js">{
 // ...,
 module: {
    // exclude node_modules
    rules: [
      {
        test: /\.(js)$/,
        exclude: /node_modules/,
        use: [&quot;babel-loader&quot;],
      },
    ],
  },
  // pass all js files through Babel
  resolve: {
    extensions: [&quot;*&quot;, &quot;.js&quot;],
  }
}</code></pre><p>Then, create the Babel config file - <code>.babelrc</code>. This is where we configure Babel to apply the <code>preset-env</code> transform.</p><pre><code class="language-json">// .babelrc

{
  &quot;presets&quot;: [
    &quot;@babel/preset-env&quot;
  ]
}</code></pre><p>Optionally, update the <code>index.js</code> to contain some ES6 features that wouldn&apos;t work without Babel &#x1F600; (actually they <a href="https://caniuse.com/es6?ref=jsramblings.com">do work in most browsers</a>, but for example <a href="https://caniuse.com/?search=array.fill&amp;ref=jsramblings.com">IE still doesn&apos;t support Array.fill</a>).</p><pre><code class="language-js">// Use a feature that needs Babel to work in all browsers :)
// arrow functions + Array fill

const sayHelloManyTimes = (times) =&gt;
  new Array(times).fill(1).map((_, i) =&gt; `Hello ${i + 1}`);

const helloDiv = document.createElement(&quot;div&quot;);
helloDiv.innerHTML = sayHelloManyTimes(10).join(&quot;&lt;br/&gt;&quot;);
document.body.append(helloDiv);
</code></pre><p>Finally, let&apos;s check everything works - run <code>npm run start</code> and check the app correctly runs:</p><figure class="kg-card kg-image-card"><img src="https://jsramblings.com/content/images/2022/06/image-2.png" class="kg-image" alt loading="lazy" width="822" height="632" srcset="https://jsramblings.com/content/images/size/w600/2022/06/image-2.png 600w, https://jsramblings.com/content/images/2022/06/image-2.png 822w" sizes="(min-width: 720px) 720px"></figure><h3 id="step-4-add-react">Step 4: Add React</h3><p>Finally, we can add React &#x1F605;</p><p>First, install it:</p><pre><code>npm i react react-dom --save
npm i @babel/preset-react --save-dev</code></pre><p>Then, update the <code>.babelrc</code> file to also apply the <code>preset-react</code> transform. This is needed, among other things, to support JSX.</p><pre><code class="language-json">// .babelrc

{
    &quot;presets&quot;: [
      &quot;@babel/preset-env&quot;,
      [&quot;@babel/preset-react&quot;, {
      &quot;runtime&quot;: &quot;automatic&quot;
    }]
    ]
}</code></pre><div class="kg-card kg-callout-card kg-callout-card-grey"><div class="kg-callout-emoji">&#x1F4A1;</div><div class="kg-callout-text">Tip: Specifying the <code>preset-react</code> runtime as <code>automatic</code> enables a <a href="https://reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html?ref=jsramblings.com">feature that no longer requires importing React on top of every file</a>.</div></div><p>Also, we need to update the Webpack config to pass <code>jsx</code> files through Babel as well:</p><pre><code class="language-js">// webpack.config.js

{
  // ...,
  module: {
    // exclude node_modules
    rules: [
      {
        test: /\.(js|jsx)$/,         // &lt;-- added `|jsx` here
        exclude: /node_modules/,
        use: [&quot;babel-loader&quot;],
      },
    ],
  },
  // pass all js files through Babel
  resolve: {
    extensions: [&quot;*&quot;, &quot;.js&quot;, &quot;.jsx&quot;],    // &lt;-- added `.jsx` here
  },
}</code></pre><p>Next, let&apos;s create a React component, so we can check that everything works:</p><pre><code class="language-JSX">// src/Hello.jsx

const Hello = () =&gt; &lt;h1&gt;Hello from React!&lt;/h1&gt;;

export default Hello;
</code></pre><p>And add it to the main app file:</p><pre><code class="language-js">// index.js

import React from &quot;react&quot;;
import { createRoot } from &quot;react-dom/client&quot;;
import Hello from &quot;./Hello&quot;;

const container = document.getElementById(&quot;root&quot;);
const root = createRoot(container);
root.render(&lt;Hello /&gt;);
</code></pre><div class="kg-card kg-callout-card kg-callout-card-grey"><div class="kg-callout-emoji">&#x1F4A1;</div><div class="kg-callout-text">Note we&apos;re using the <a href="https://reactjs.org/blog/2022/03/08/react-18-upgrade-guide.html?ref=jsramblings.com#updates-to-client-rendering-apis">new React 18 syntax with <code>createRoot</code></a>.</div></div><p>Finally, we also update the <code>index.html</code> to provide a &quot;root&quot; node for the app:</p><pre><code class="language-html">&lt;!-- index.html --&gt;
&lt;!-- ... --&gt;
&lt;body&gt;
    &lt;div id=&quot;root&quot;&gt;&lt;/div&gt;
&lt;/body&gt;</code></pre><p>Let&apos;s check that it works - run <code>npm run start</code> and you should see &quot;Hello from React!&quot;:</p><figure class="kg-card kg-image-card"><img src="https://jsramblings.com/content/images/2022/06/image-3.png" class="kg-image" alt loading="lazy" width="828" height="288" srcset="https://jsramblings.com/content/images/size/w600/2022/06/image-3.png 600w, https://jsramblings.com/content/images/2022/06/image-3.png 828w" sizes="(min-width: 720px) 720px"></figure><p>Also, check there are no errors in the console.</p><p>You can also check that the app correctly runs in production, by running <code>npm run build</code> and then <code>npx serve build</code>.</p><h3 id="thats-it">That&apos;s it!</h3><p>See the full code for this tutorial on Github: <a href="https://github.com/jsramblings/react-webpack-setup?ref=jsramblings.com">https://github.com/jsramblings/react-webpack-setup</a></p>]]></content:encoded></item><item><title><![CDATA[Authentication with Nodejs and JWTs - a simple example]]></title><description><![CDATA[When it comes to JWTs, most tutorials help you build a full implementation in Node. However, they rarely stop to show just the basics - just the esssential parts that need to be there for JWT authentication to work, and nothing more.]]></description><link>https://jsramblings.com/authentication-with-node-and-jwt-a-simple-example/</link><guid isPermaLink="false">64ecfadfa81cfd3240134830</guid><category><![CDATA[Authentication]]></category><dc:creator><![CDATA[Corina]]></dc:creator><pubDate>Wed, 26 Jan 2022 08:35:40 GMT</pubDate><content:encoded><![CDATA[<p>When it comes to JWTs, most tutorials help you build a full implementation in Node. However, they rarely stop to show just the basics - just the esssential parts that need to be there for JWT authentication to work, and nothing more.</p><p>This article aims to give a simple, straightforward overview of the steps needed to add JWT auth to a Node app.</p><p>We&apos;ll start from an unprotected API with a super secret resource and go towards securing it with JSON Web Tokens (JWTs).</p><h3 id="what-well-build">What we&apos;ll build</h3><p>Our API will only contain two endpoints:</p><ul><li><code>/login</code> - will return the token</li><li><code>/super-secret-resource</code> - the information that only logged in users should access</li></ul><h3 id="step-1-creating-a-node-api-with-express">Step 1: Creating a Node API with Express</h3><p>Let&apos;s start from the barebones - just an API that returns a very important resource, that should only be accessed by logged in users. This endpoint should return <code>401 Unauthorized</code> if the user is not logged in. Since we don&apos;t have the <code>/login</code> endpoint yet, it will just always return 401 for now.</p><p>Create a new folder for the project, run <code>npm init -y</code> to initialize the project and run <code>npm install express</code> to install <code>express</code>.</p><p>Next, create a <code>server.js</code> file for the node app. In its simplest form, this is how the API would look:</p><pre><code class="language-javascript">// server.js
const express = require(&quot;express&quot;);
const app = express();

app.get(&quot;/super-secure-resource&quot;, (req, res) =&gt; {
  return res
    .status(401)
    .json({ message: &quot;You need to be logged in to access this resource&quot; });
});

app.listen(3001, () =&gt; {
  console.log(&quot;API running on localhost:3001&quot;);
});
</code></pre><p>To start the API, &#xA0;run <code>node server.js</code> in the console.</p><p>To check that the endpoint cannot be accessed, run <code>curl</code>:</p><pre><code class="language-bash">curl -i localhost:3001/super-secure-resource

// Will output:
// 
// HTTP/1.1 401 Unauthorized
// X-Powered-By: Express
// Content-Type: application/json; charset=utf-8
// Content-Length: 62
// ETag: W/&quot;3e-eJQzoUv1nk6AKpsmnnXBmFvKrI4&quot;
// Date: Sat, 22 Jan 2022 09:05:53 GMT
// Connection: keep-alive
// Keep-Alive: timeout=5
// 
// {&quot;message&quot;:&quot;You need to be logged in to access this resource&quot;}</code></pre><p>The <code>-i</code> flag tells <code>curl</code> to also return the headers, so we can see the HTTP status code.</p><p>Notice the <code>401</code> HTTP status code - which stands for &quot;Unauthorized&quot; - and the message we returned from the endpoint.</p><h3 id="step-2-return-a-jwt-token-on-successful-login">Step 2: Return a JWT token on successful login</h3><p>Next, we want to allow users to login and send back a verified token if their user and password are correct.</p><p>Since setting up a full database of users and password is out of the scope of this article, we&apos;ll just hardcode an <code>admin/admin</code> user and check for that as an example.</p><p>The first step is to install the npm <a href="https://www.npmjs.com/package/jsonwebtoken?ref=jsramblings.com">jsonwebtoken</a> module. This package will help us sign and verify JWT tokens.</p><pre><code>npm install jsonwebtoken</code></pre><p>Then, the endpoint will look like this:</p><pre><code class="language-javascript">const express = require(&quot;express&quot;);
const jsonwebtoken = require(&quot;jsonwebtoken&quot;);

// The secret should be an unguessable long string (you can use a password generator for this!)
const JWT_SECRET =
  &quot;goK!pusp6ThEdURUtRenOwUhAsWUCLheBazl!uJLPlS8EbreWLdrupIwabRAsiBu&quot;;

const app = express();
app.use(express.json());

app.post(&quot;/login&quot;, (req, res) =&gt; {
  const { username, password } = req.body;
  console.log(`${username} is trying to login ..`);

  if (username === &quot;admin&quot; &amp;&amp; password === &quot;admin&quot;) {
    return res.json({
      token: jsonwebtoken.sign({ user: &quot;admin&quot; }, JWT_SECRET),
    });
  }

  return res
    .status(401)
    .json({ message: &quot;The username and password your provided are invalid&quot; });
});
</code></pre><p>Some things to note here:</p><ul><li>the <code>JWT_SECRET</code> should be an unguessable long string</li><li>the part that actually does the signing is: <code>jsonwebtoken.sign({ user: &quot;admin&quot; }, JWT_SECRET)</code></li></ul><p>We can check a token is returned with curl:</p><pre><code class="language-bash">curl -X POST http://localhost:3001/login --header &quot;Content-Type: application/json&quot; --data &apos;{ &quot;username&quot;: &quot;admin&quot;, &quot;password&quot;: &quot;admin&quot; }&apos;

// {&quot;token&quot;:&quot;eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYWRtaW4iLCJpYXQiOjE2NDI4NDQ0NTR9.O_710gS-6t9nmqvW0_E1GlP7MdoLMFR85GXUUeskNi8&quot;}
</code></pre><p>You can decode the token and see what it contains on jwt.io</p><figure class="kg-card kg-image-card"><img src="https://jsramblings.com/content/images/2022/01/jwt-io-decoded-token.png" class="kg-image" alt loading="lazy" width="2000" height="1302" srcset="https://jsramblings.com/content/images/size/w600/2022/01/jwt-io-decoded-token.png 600w, https://jsramblings.com/content/images/size/w1000/2022/01/jwt-io-decoded-token.png 1000w, https://jsramblings.com/content/images/size/w1600/2022/01/jwt-io-decoded-token.png 1600w, https://jsramblings.com/content/images/size/w2400/2022/01/jwt-io-decoded-token.png 2400w" sizes="(min-width: 720px) 720px"></figure><p>Some things to note:</p><ul><li>The &quot;Header&quot; of the token contains the algorithm used - in our case, the algorithm for signing the token is HS256</li><li>You can input your secret in the bottom right to have jwt.io verify the token signature (if the signature is verified, it means the token hasn&apos;t been tampered with)</li><li>Besides the <code>{ user: admin }</code> which we encoded, there&apos;s also another property there - <code>iat</code>, added by the <code>jsonwebtoken</code> library; this stands for &quot;Issued at&quot; - i.e. when the server created the token (as Unix timestamp)</li></ul><h3 id="step-3-send-the-token-to-the-super-secure-resource-and-verify-it-to-allow-access">Step 3: Send the token to the /super-secure-resource and verify it to allow access</h3><p>Now let&apos;s use that token to allow the logged in user to access the restricted resource!</p><p>In order to do that, we will need to send the token to the API (as an <code>Authorization</code> header) and then verify the token on the server:</p><pre><code class="language-js">import jwt from &quot;jsonwebtoken&quot;;

app.get(&quot;/super-secure-resource&quot;, (req, res) =&gt; {
  if (!req.headers.authorization) {
    return res.status(401).json({ error: &quot;Not Authorized&quot; });
  }

  // Bearer &lt;token&gt;&gt;
  const authHeader = req.headers.authorization;
  const token = authHeader.split(&quot; &quot;)[1];

  try {
    // Verify the token is valid
    const { user } = jwt.verify(token, process.env.JWT_SECRET);
    return res.status(200).json({
      message: `Congrats ${user}! You can now accesss the super secret resource`,
    });
  } catch (error) {
    return res.status(401).json({ error: &quot;Not Authorized&quot; });
  }
});
</code></pre><p>When doing <code>curl</code>, the resource will now be accessible &#x1F4AA;:</p><pre><code>curl -i localhost:3001/super-secure-resource --Header &apos;Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYWRtaW4iLCJpYXQiOjE2NDI4NDYzMzV9.gQj-qjoQwtvyU2XzER6yiT-T4DwYphjMft-kogW978c&apos;

// Will return:
//
// HTTP/1.1 200 OK
// X-Powered-By: Express
// Content-Type: application/json; charset=utf-8
// Content-Length: 69
// ETag: W/&quot;45-9rvFFDk8vkfruRd3Ok0vQNGIdMk&quot;
// Date: Sat, 22 Jan 2022 10:15:03 GMT
// Connection: keep-alive
// Keep-Alive: timeout=5
//
// {&quot;message&quot;:&quot;Congrats! You can now accesss the super secret resource&quot;}
</code></pre><p>Why this works:</p><ul><li>By signing the token, you ensure that the server can check that whatever token it receives, was created by itself</li><li>If you try to tamper with the token, you&apos;ll invalidate it! The server will tell it&apos;s been tampered, so it will not authorise you!</li><li>Try it out: take the token, change it in jwt.io and try again -&gt; it will fail!</li></ul><h3 id="make-it-yours">Make it yours!</h3><p>I specifically kept the source code for this article very barebones. Go ahead and improve it!</p><ul><li>Add <code>nodemon</code> to automatically reload the server when you change the API</li><li>Add <code>nodeenv</code> and move the JWT_SECRET to as an environment variable</li><li>Setup an <em>actual</em> database with users and passwords</li><li>Add more endpoints and move the code that verifies the token in an express middleware</li></ul>]]></content:encoded></item><item><title><![CDATA[How to check if your component rerendered - and why!]]></title><description><![CDATA[<p>If you&apos;re like me, you&apos;re probably using <code>console.log</code> a lot to check the React rendering lifecycle of your components &#x1F605; When it rerendered, why - was it a <code>setState</code> or something else ?! - or even just to get a better handle of React fundamentals.</p><p>This</p>]]></description><link>https://jsramblings.com/how-to-check-if-your-component-rerendered-and-why/</link><guid isPermaLink="false">64ecfadfa81cfd324013482f</guid><dc:creator><![CDATA[Corina]]></dc:creator><pubDate>Mon, 11 Oct 2021 18:00:55 GMT</pubDate><content:encoded><![CDATA[<p>If you&apos;re like me, you&apos;re probably using <code>console.log</code> a lot to check the React rendering lifecycle of your components &#x1F605; When it rerendered, why - was it a <code>setState</code> or something else ?! - or even just to get a better handle of React fundamentals.</p><p>This <em>can</em> work fine at the beginning - but what if you want to answer bigger questions about your app - like whether using Context has an impact on the performance of your app? Or simply, what if you&apos;re tired of scrolling through tens of <code>console.log</code>s figuring out what executed when?</p><p>Then it&apos;s time to use the React DevTools! Not only do they allow you to visually see what components rerender, but you can also investigate <em>what</em> caused each rerender and easily spot what components are taking longest to render.</p><h2 id="using-react-devtools-to-highlight-what-components-rerendered">Using React DevTools to highlight what components rerendered</h2><p>There&apos;s a checkbox well hidden in the React DevTools settings that allows you to visually highlight the components that rerendered. </p><p>To enable it, go to &quot;Profiler&quot; &gt;&gt; click the &quot;Cog wheel&quot; on the right side of the top bar &gt;&gt; &quot;General&quot; tab &gt;&gt; Check the &quot;Highlight updates when components render.&quot; checkbox. </p><p>Then just interact with your app as usual and watch it all light up &#x2728;</p><!--kg-card-begin: html--><figure class="kg-card kg-image-card">
    <video autoplay loop muted playsinline>
		<source src="https://www.dropbox.com/sh/6iyj99t1zdawg2h/AAA71EWo4YTZHgwEdx5sXzmxa/enable-highlighting.mp4?raw=1" type="video/mp4" class="kg-image">
	</video>
</figure>

<!--kg-card-end: html--><h2 id="using-react-devtools-to-find-out-the-cause-of-a-rerender">Using React DevTools to find out the cause of a rerender</h2><p>Now that you can <em>see</em> your component rerendered, the next question is .. why?</p><p>There&apos;s a very handy tooltip that shows up when hovering over a component in the &quot;Flamegraph&quot;, that shows the reason for a render under the &quot;Why did this render?&quot; heading.</p><figure class="kg-card kg-image-card"><img src="https://jsramblings.com/content/images/2021/10/why-did-this-render-tooltip.png" class="kg-image" alt loading="lazy" width="2000" height="1392" srcset="https://jsramblings.com/content/images/size/w600/2021/10/why-did-this-render-tooltip.png 600w, https://jsramblings.com/content/images/size/w1000/2021/10/why-did-this-render-tooltip.png 1000w, https://jsramblings.com/content/images/size/w1600/2021/10/why-did-this-render-tooltip.png 1600w, https://jsramblings.com/content/images/size/w2400/2021/10/why-did-this-render-tooltip.png 2400w" sizes="(min-width: 720px) 720px"></figure><p>To see this, you need to record a &quot;Profiler&quot; session.</p><p>For example, if you your component re-renders after you click a button, you can click record to start profiling, click the button in question, then stop profiling and see the details of what happened when the button was clicked.</p><!--kg-card-begin: html--><figure class="kg-card kg-image-card">
    <video autoplay loop muted playsinline>
		<source src="https://www.dropbox.com/s/p6md04o8imwy3ns/why-did-this-render-profiler-session.mp4?raw=1" type="video/mp4" class="kg-image">
	</video>
</figure>

<!--kg-card-end: html--><p>Here&apos;s how to read the profiler output:</p><ul><li>In the top right you have the number of React commits - each bar represents a moment when the React component tree changed and a change was commited to the DOM</li><li>The &quot;Flamegraph&quot; shows the component tree and what changed - &quot;grey&quot; means the component did not rerender</li></ul><p>Hovering over each component will reveal why it rendered in the &quot;Why did this render?&quot; subheading.</p><p><a href="https://github.com/facebook/react/blob/033efe7312cdf73118922b279d9b1ae29a2f693d/packages/react-devtools-shared/src/devtools/views/Profiler/WhatChanged.js?ref=jsramblings.com#L40">Possible values</a> you can expect to see as reasons for a component rerendering:</p><ul><li>&quot;This is the first time the component rendered.&quot;</li><li>&quot;Context changed&quot;</li><li>&quot;Hooks changed&quot;</li><li>&quot;Props changed&quot;</li><li>&quot;State changed&quot;</li><li>&quot;The parent component rendered.&quot;</li></ul><p>You&apos;ll notice that sometimes the reason is not very detailed - simply saying &quot;hook changed&quot; instead of the details of <em>what</em> hooked changed. This is expected and it happens because sometimes the DevTools simply does not have access to that information or because it would be too slow to retrieve. </p><p>When you click a component in the &quot;Flamegraph&quot;, the right sidebar will update to show all renders of that component instance and why they happened:</p><figure class="kg-card kg-image-card"><img src="https://jsramblings.com/content/images/2021/10/why-did-this-render-sidebar-devtools.png" class="kg-image" alt loading="lazy" width="2000" height="376" srcset="https://jsramblings.com/content/images/size/w600/2021/10/why-did-this-render-sidebar-devtools.png 600w, https://jsramblings.com/content/images/size/w1000/2021/10/why-did-this-render-sidebar-devtools.png 1000w, https://jsramblings.com/content/images/size/w1600/2021/10/why-did-this-render-sidebar-devtools.png 1600w, https://jsramblings.com/content/images/size/w2400/2021/10/why-did-this-render-sidebar-devtools.png 2400w" sizes="(min-width: 720px) 720px"></figure><h2 id="bonus-tip-see-at-a-glance-what-components-took-longest-to-render">Bonus tip: See at a glance what components took longest to render</h2><p>It took me a while to figure this one out, but the colors in the &quot;Flamegraph&quot; actually have a meaning: &quot;cool&quot; colors - like shades of blue - signify the component took little time to render compared with everything else and &quot;warm&quot; colors - like orange and red - signify the component took a longer time to render &#x1F600;</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://jsramblings.com/content/images/2021/10/render-duration.png" class="kg-image" alt loading="lazy" width="2000" height="201" srcset="https://jsramblings.com/content/images/size/w600/2021/10/render-duration.png 600w, https://jsramblings.com/content/images/size/w1000/2021/10/render-duration.png 1000w, https://jsramblings.com/content/images/size/w1600/2021/10/render-duration.png 1600w, https://jsramblings.com/content/images/size/w2400/2021/10/render-duration.png 2400w" sizes="(min-width: 720px) 720px"><figcaption>flamechart color - yellow took longest</figcaption></figure><p>Notice how I just said &quot;more time&quot; or &quot;less time&quot; to render - that&apos;s because what is slow or fast is relative to your specific app and total render time. So it could be that a &quot;yellow&quot; component rerendered very fast if it&apos;s part of a simple component tree, or for a &quot;blue&quot; component to actually take a very long time to render if the whole app is very slow.</p><h2 id="bonus-tip-see-what-dom-changes-actually-happened">Bonus tip: See what DOM changes actually happened</h2><p>Even if a component rerendered, it doesn&apos;t mean that any changes were actually applied to the DOM - it could be that the output of <code>render</code> was the same as before, in which case React does nothing. But how can you check? How can you visually see what DOM parts were rerendered?</p><p>The Chrome DevTools have a very useful setting for this - &quot;Paint flashing&quot;. This will highlight all elements of a page that were &quot;repainted&quot;. To enable it, you need to click the &quot;Settings&quot; menu, then &quot;More settings&quot; and then &quot;Rendering&quot; and check the &quot;Enable paint flashing&quot; checkbox.</p><!--kg-card-begin: html--><figure class="kg-card kg-image-card">
    <video autoplay loop muted playsinline>
		<source src="https://www.dropbox.com/s/5odb4gmayz7pmsq/paint-flashing.mp4?raw=1" type="video/mp4" class="kg-image">
	</video>
</figure>

<!--kg-card-end: html--><h2 id="try-it-out-for-yourself-">Try it out for yourself!</h2><p>You can find the sample app used for this blog post at <a href="https://stackblitz.com/edit/react-3cmy6b?ref=jsramblings.com">https://stackblitz.com/edit/react-3cmy6b</a>. (Make sure to click &quot;Open in New Window&quot; in the top bar in order to see the app outside of the StackBlitz environment, which makes it easier to profile. )</p><p>Can you figure out why all &quot;cards&quot; render when just one is clicked? And moreover, can you fix it? &#x1F600;</p><p>Fork the code and share your solution in the comments below!</p>]]></content:encoded></item><item><title><![CDATA[Should you switch to useReducer or is useState enough for your needs?]]></title><description><![CDATA[<p>The <code>useReducer</code> hook is usually recommended when the state becomes complex, with state values depending on each other or when the next state depends on the previous one. However, many times you can simply bundle your separate <code>useState</code> statements into one object and continue to use <code>useState</code>. So when is</p>]]></description><link>https://jsramblings.com/should-you-switch-to-usereducer-or-is-usestate-enough-for-your-needs/</link><guid isPermaLink="false">64ecfadfa81cfd324013482e</guid><dc:creator><![CDATA[Corina]]></dc:creator><pubDate>Mon, 13 Sep 2021 07:40:47 GMT</pubDate><content:encoded><![CDATA[<p>The <code>useReducer</code> hook is usually recommended when the state becomes complex, with state values depending on each other or when the next state depends on the previous one. However, many times you can simply bundle your separate <code>useState</code> statements into one object and continue to use <code>useState</code>. So when is the extra boilerplate required for <code>useReducer</code> justified after all?</p><h3 id="when-there-is-complex-business-logic">When there is complex business logic</h3><p>Structuring your code with actions allows for better encapsulation of business logic.</p><p>Also, it allows you to separate the description of the action (WHAT happened) from how the state should update because of it (HOW it should be handled).</p><p>A simple example to showcase this is updating the state when the user navigates to the next page on a paginated list view.</p><p>With the  hook, this could look like this:</p><pre><code class="language-jsx">setState(state =&gt; { ...state, page + 1})</code></pre><p>However, with actions, you can add more semantics to this event and encapsulate the logic:</p><pre><code class="language-jsx">// action
dispatch({ type: &apos;GO_TO_NEXT_PAGE&apos; })
// reducer
case &apos;GO_TO_NEXT_PAGE&apos;:
  return { ...state, page: state.page + 1}</code></pre><p>In the future, if you decide something else needs to happen when the user clicks the &quot;Next page&quot; button, you only need to update one place: the reducer.</p><p>While this is a simple example, for deep nested values or arrays of objects, abstracting away the state update logic can be very valuable.</p><h3 id="when-there-are-many-ways-the-state-can-be-updated">When there are many ways the state can be updated</h3><p>In most situations, there are only a couple events that update the state - take for an example a simple login flow, where you only need to track loading, success and error states. In this case, &#xA0;<code>useReducer</code> does not add that much value.</p><p>However, imagine a more complex login process, like multi-factor authentication, where the user needs to be guided over several screens - to enter his password, to enter the one time token - and you need to handle all sorts of edge cases like the QR code expiring, the password being incorrect etc.</p><p>In these situations reducers can really make it easier to follow the different component states and how they change.</p><h3 id="when-you-need-better-debugging">When you need better debugging</h3><p>Tracking how the component state changes after each <code>setState</code> &#xA0;is generally harder to do than just having all changes go through a reducer.</p><p>Regardless if you prefer using <code>console.log</code> &#xA0;or breakpoints, it&apos;s easier to add just one log/breakpoint in the reducer as opposed to one for each &#xA0;call sprinkled across the component.</p><p>And if you ever decide to switch to Redux, the Redux DevTools make for a great debugging experience.</p><p></p><p>To wrap up, you don&apos;t need to jump to <code>useReducer</code> as soon as you have multiple <code>useState</code> hooks in your component. Many times, it&apos;s enough to just bundle all the state values in one object. However, if it becomes hard to figure out what&apos;s going on, <code>useReducer</code> can be a very helpful tool!</p><p></p><p>If you&apos;re interested to read more, I&apos;ve found these articles helpful in my research:</p><ul><li><a href="https://reactjs.org/docs/hooks-reference.html?ref=jsramblings.com#usereducer">https://reactjs.org/docs/hooks-reference.html#usereducer</a></li><li><a href="https://reactjs.org/docs/hooks-faq.html?ref=jsramblings.com#how-to-avoid-passing-callbacks-down">https://reactjs.org/docs/hooks-faq.html#how-to-avoid-passing-callbacks-down</a></li><li><a href="https://overreacted.io/a-complete-guide-to-useeffect/?ref=jsramblings.com#why-usereducer-is-the-cheat-mode-of-hooks">https://overreacted.io/a-complete-guide-to-useeffect/#why-usereducer-is-the-cheat-mode-of-hooks</a></li><li><a href="https://kentcdodds.com/blog/should-i-usestate-or-usereducer?ref=jsramblings.com">https://kentcdodds.com/blog/should-i-usestate-or-usereducer</a></li><li><a href="https://www.robinwieruch.de/react-usereducer-vs-usestate?ref=jsramblings.com">https://www.robinwieruch.de/react-usereducer-vs-usestate</a></li></ul>]]></content:encoded></item><item><title><![CDATA[Should you deep clone nested properties?]]></title><description><![CDATA[<p>You already know you&apos;re not supposed to mutate state directly when you update the state in React.</p><p>However, what if one of the members of the object you&apos;re updating is a reference?</p><p>Do you have to create a new version of that object/array as well?</p>]]></description><link>https://jsramblings.com/should-you-deep-clone-nested-properties/</link><guid isPermaLink="false">64ecfadfa81cfd324013482d</guid><dc:creator><![CDATA[Corina]]></dc:creator><pubDate>Sat, 07 Aug 2021 06:32:51 GMT</pubDate><content:encoded><![CDATA[<p>You already know you&apos;re not supposed to mutate state directly when you update the state in React.</p><p>However, what if one of the members of the object you&apos;re updating is a reference?</p><p>Do you have to create a new version of that object/array as well?</p><pre><code class="language-javascript">const [order, setOrder] = useState({ 
  id: &apos;12345&apos;,
  customer: &apos;John Doe&apos;,
  tags: [&apos;black&apos;, &apos;day-trip&apos;],	// &lt;-- this is a reference!
  shippingAddress: {			// &lt;-- this is also a reference
    street: &apos;Steiner St. 27&apos;,
    city: &apos;San Francisco&apos;
  }
})</code></pre><h3 id="you-don-t-need-to-deep-clone-when-mutating-state">You don&apos;t need to deep clone when mutating state</h3><p>If you only need to change one of the basic props of your object, it&apos;s ok to leave everything else the same - i.e. shallow copy the other properties that are nested.</p><pre><code class="language-javascript">const newOrder = { ...order, name: &apos;John Doe The Second&apos;}</code></pre><h3 id="-unless-the-nested-property-actually-changed">... unless the nested property actually changed</h3><p>But what if something inside the nested reference changes? Then indeed, you would &#xA0;need to deep clone that:</p><pre><code class="language-javascript">// Let&apos;s add a new tag
const newOrder = { 
  ...order, 
  tags: [
    ...order.tags, 
    &apos;some-new-tag&apos;
  ]
};

// .. or update the shipping address
const newOrder = { 
  ...order, 
  shippingAddress: { 
    ...order.shippingAddress, 
    city: &apos;New York&apos;
}}</code></pre><h3 id="but-why-is-it-that-you-don-t-need-to-deep-clone">But why is it that you don&apos;t need to deep clone?</h3><p>You&apos;ve seen that it&apos;s enough to do shallow copies of nested properties if they haven&apos;t changed .. but why?</p><p>It all boils down to why you create copies in the first place - to let React know that your state changed so it can rerender. If you also create new references for nested props as well, React will assume those changed as well, and rerender them!</p><p>So if you had a component showing the list of tags or the shipping address, and you would deep copy those when updating the state, React would re-render them even if they hadn&apos;t changed, just because a new reference was passed!</p><h3 id="further-reading">Further reading</h3><p>I hope you found this useful! If you want to learn more about how references work and immutably changing state, make sure to check out these links as well:</p><ul><li><a href="https://daveceddia.com/javascript-references/?ref=jsramblings.com">https://daveceddia.com/javascript-references/</a></li><li><a href="https://redux.js.org/faq/performance?ref=jsramblings.com#do-i-have-to-deep-clone-my-state-in-a-reducer-isnt-copying-my-state-going-to-be-slow">https://redux.js.org/faq/performance#do-i-have-to-deep-clone-my-state-in-a-reducer-isnt-copying-my-state-going-to-be-slow</a></li></ul>]]></content:encoded></item><item><title><![CDATA[How to use useState hook in React with Typescript]]></title><description><![CDATA[<p>I recently stumbled upon this question on Stack Overflow:</p><blockquote>I am trying to make a login form in react with typescript. But setEmail method is not accepting value. It says Argument of type &apos;string&apos; is not assignable to parameter of type &apos;SetStateAction&apos;. What should I do</blockquote>]]></description><link>https://jsramblings.com/how-to-use-usestate-hook-in-react-with-typescript/</link><guid isPermaLink="false">64ecfadfa81cfd324013482c</guid><dc:creator><![CDATA[Corina]]></dc:creator><pubDate>Sat, 31 Jul 2021 09:43:13 GMT</pubDate><content:encoded><![CDATA[<p>I recently stumbled upon this question on Stack Overflow:</p><blockquote>I am trying to make a login form in react with typescript. But setEmail method is not accepting value. It says Argument of type &apos;string&apos; is not assignable to parameter of type &apos;SetStateAction&apos;. What should I do to solve it?</blockquote><p>If you&apos;ve used React with Typescript before, you&apos;ve probably seen it yourself as well.</p><figure class="kg-card kg-image-card"><img src="https://jsramblings.com/content/images/2021/07/Screenshot-2021-07-31-at-07.50.48.png" class="kg-image" alt loading="lazy" width="1656" height="880" srcset="https://jsramblings.com/content/images/size/w600/2021/07/Screenshot-2021-07-31-at-07.50.48.png 600w, https://jsramblings.com/content/images/size/w1000/2021/07/Screenshot-2021-07-31-at-07.50.48.png 1000w, https://jsramblings.com/content/images/size/w1600/2021/07/Screenshot-2021-07-31-at-07.50.48.png 1600w, https://jsramblings.com/content/images/2021/07/Screenshot-2021-07-31-at-07.50.48.png 1656w" sizes="(min-width: 720px) 720px"></figure><h3 id="so-what-s-going-on-what-does-the-error-message-mean">So what&apos;s going on? What does the error message mean?</h3><p>If I were to rewrite that error message and make it more verbose, it would sound something like &quot;the value <code>e.target.value</code> (of type <code>string</code>) cannot be passed as an argument to the <code>setEmail</code> function - since its type is <code>SetStateAction&lt;undefined&gt;</code>, which means it expects an <code>undefined</code> value&quot;</p><p>While rewriting the error message clarifies a bit what it refers to, it still makes no sense that it would expect an <code>undefined</code> value to be passed! Why is that?</p><p></p><h3 id="why-does-setemail-expect-an-undefined-value">Why does setEmail expect an undefined value?</h3><p>It&apos;s because when defining the <code>email</code> state, no type was explicitly defined.</p><p>Theoretically, when using the <code>useState</code> hook with Typescript, you would need to explicitly specify the type of state you&apos;re defining:</p><pre><code class="language-JSX">const [email, setEmail] = useState&lt;string&gt;();</code></pre><p>If you don&apos;t do this, Typescript will try to just guess what value you&apos;re expecting. That&apos;s why it will fill the blanks for you and &quot;translate&quot; <code>useState()</code> to <code>useState&lt;undefined&gt;()</code>.</p><p></p><h3 id="does-this-mean-you-always-need-to-pass-in-the-type-when-calling-usestate">Does this mean you always need to pass in the type when calling useState?</h3><p>Not necessarily. If you pass in an initial state value, Typescript might be able to correctly guess the type.</p><p>I said before that given <code>useState()</code>, Typescript will just assume you meant <code>useState&lt;undefined&gt;()</code>.</p><p>However, if you passed an empty string as an initial value - &#xA0;<code>useState(&apos;&apos;)</code> -, Typescript will be able to figure out you passed a string and will assume you mean <code>useState&lt;string&gt;(&apos;&apos;)</code>.</p><p>This is called <a href="https://www.typescriptlang.org/docs/handbook/type-inference.html?ref=jsramblings.com">Typescript inference</a> and it&apos;s quite powerful.</p><p></p><h3 id="fixing-the-error">Fixing the error</h3><p>So, to wrap up, in order to fix the error, you have two options:</p><p>1. Explicitly type the state when initially declaring it:</p><pre><code class="language-JSX">const [email, setEmail] = useState&lt;string&gt;();</code></pre><p>2. Help Typescript infer the state type by passing in an initial value:</p><pre><code class="language-JSX">const [email, setEmail] = useState(&apos;&apos;);</code></pre><p>That&apos;s it! I hope you found this useful. Let me know in the comments below if you still have any questions :)</p>]]></content:encoded></item><item><title><![CDATA[React not batching your setState updates? Here's what you can do about it]]></title><description><![CDATA[<p>Say you&apos;re calling two state update functions, one after the other</p><pre><code class="language-JSX">const [data, setData] = useState(null);
const [isLoading, setLoading] = useState(false);

// on receiving the API response 
setIsLoading(false);
setData(data);
</code></pre><p>Depending on whether your code runs as part of an event handler or inside a regular function, this</p>]]></description><link>https://jsramblings.com/react-not-batching-your-setstate-updates-heres-what-you-can-do-about-it/</link><guid isPermaLink="false">64ecfadfa81cfd324013482b</guid><dc:creator><![CDATA[Corina]]></dc:creator><pubDate>Sat, 31 Jul 2021 06:59:39 GMT</pubDate><content:encoded><![CDATA[<p>Say you&apos;re calling two state update functions, one after the other</p><pre><code class="language-JSX">const [data, setData] = useState(null);
const [isLoading, setLoading] = useState(false);

// on receiving the API response 
setIsLoading(false);
setData(data);
</code></pre><p>Depending on whether your code runs as part of an event handler or inside a regular function, this will trigger two rerenders or just one, based on whether React batches the <code>setState</code> calls or not.</p><blockquote>Until React 18, we only batched updates during the React event handlers. Updates inside of promises, setTimeout, native event handlers, or any other event were not batched in React by default.<br><a href="https://github.com/reactwg/react-18/discussions/21?ref=jsramblings.com">https://github.com/reactwg/react-18/discussions/21</a></blockquote><p>This behaviour will change in React 18, but until then, what can you do to prevent multiple unnecessary rerenders and potentially invalid states?</p><h3 id="approach-1-group-related-state-variables-in-one-state-object">Approach 1: Group related state variables in one state object</h3><p>It&apos;s a good practice to analyse the things you keep in the state and see whether they change together or not.</p><p>In this case, the <code>data</code> and <code>isLoading</code> variables do change together - we always want the loading to stop when the data is received</p><p>Thus, it makes sense to create just one <code>state</code> for both:</p><pre><code class="language-JSX">const [response, setResponse] = useState({ data: null, isLoading: false  })

// on receiving the API response
setResponse({ ...response, data, isLoading: false })</code></pre><p>While this works for very simple cases, in practice, it can become tedious and error prone, since you need to manually merge the previous state and the new changed values every time.</p><p>This is way a better approach is to switch to using the <code>useReducer</code> hook.</p><h3 id="approach-2-refactor-your-code-to-use-the-usereducer-hook">Approach 2: Refactor your code to use the useReducer hook</h3><p>Let&apos;s see how the same example would look using <code>useReducer</code>:</p><pre><code class="language-JSX">const reducer = (state, action) =&gt; {
  switch(action.type) {
    case &apos;API_SUCCESS&apos;:
      return {
        ...state,
        data: action.payload.data,
        isLoading: false
      }
    default:
      return state;
  }
}

const initialState = { data: null, isLoading: false}
const [state, dispatch] = useReducer(reducer, initialState)

// on receiving the API response 
dispatch({ type: &apos;API_SUCCESS&apos;, payload: { data }})</code></pre><p>While this does increase the complexity of the code a little, in the long term it might be a better choice:</p><ul><li>it makes it easy to extend the component with new functionality: for example, handling the error states can be added with a few lines changes in the <code>reducer</code></li><li>it makes the component state more predictable: all state transitions are defined in one single place, making it easier to reason about how the state changes</li><li>it prevents invalid state values (e.g. <code>isLoading</code> being <code>false</code>, but the <code>data</code> not being updated yet) and unnecessary rerenders, as the state is only updated once no matter how many values changed</li></ul><p>I hope you found this useful!</p><p>If you&apos;d like to read more on the topic, make sure to take a look <a href="https://github.com/reactwg/react-18/discussions/21?ref=jsramblings.com">how batching will work in React 18</a>.</p>]]></content:encoded></item><item><title><![CDATA[Are you logging the state immediately after updating it? Here's why that doesn't work]]></title><description><![CDATA[<p>Did you ever stumble upon a situation where it seems no matter what you do, your state does not get correctly updated?</p><pre><code class="language-JSX">import React, { useState, useEffect } from &apos;react&apos;

const PokemonList = () =&gt; {
    const [pokemons, setPokemons] = useState([]);
    useEffect(async () =&gt; {
        const data = await fetch(`https://pokeapi.co/api/v2/pokemon?</code></pre>]]></description><link>https://jsramblings.com/are-you-logging-the-state-immediately-after-updating-it-heres-why-that-doesnt-work/</link><guid isPermaLink="false">64ecfadfa81cfd3240134828</guid><dc:creator><![CDATA[Corina]]></dc:creator><pubDate>Sat, 31 Jul 2021 06:45:53 GMT</pubDate><content:encoded><![CDATA[<p>Did you ever stumble upon a situation where it seems no matter what you do, your state does not get correctly updated?</p><pre><code class="language-JSX">import React, { useState, useEffect } from &apos;react&apos;

const PokemonList = () =&gt; {
    const [pokemons, setPokemons] = useState([]);
    useEffect(async () =&gt; {
        const data = await fetch(`https://pokeapi.co/api/v2/pokemon?limit=10`).then(response =&gt; response.json());
        setPokemons(data.results);
        console.log(&apos;New pokemons list &apos;, pokemons);
    }, [])
    
    return &lt;ul&gt;{pokemons.map(pokemon =&gt; &lt;li key={pokemon.name}&gt;{pokemon.name}&lt;/li&gt;)}&lt;/ul&gt;
}</code></pre><p>The API returns correctly, you update your state by calling <code>setState</code>, yet when you log the value, it always comes back empty?</p><p>Well, maybe everything works fine just that you&apos;re logging at the wrong time &#x1F648;</p><h3 id="why-does-this-happen">Why does this happen?</h3><p>When logging inside &#xA0;<code>useEffect</code>, you&apos;re using the <code>state</code> value that was captured in the closure at the beginning of the render. So that&apos;s why you&apos;re always seeing the empty array.</p><p>Your code is probably working just fine, and it&apos;s just the <code>console.log</code> looking at an outdated value!</p><h3 id="how-to-fix-this">How to fix this</h3><p>Moving the <code>console.log</code> right before the <code>return</code> statement will instead look at the latest <code>state</code> value and log the updated list of pokemons &#x1F4A5;</p><h3 id="deep-dive-logging-inside-useeffect">Deep dive: Logging inside useEffect</h3><p>Let&apos;s do a step by step walkthrough of what happens when the component is rendered. Notice I added a lot of extra <code>console.log</code> statements:</p><pre><code class="language-JSX">import React, { useState, useEffect } from &apos;react&apos;

const PokemonList = () =&gt; {
    console.log(&apos;======= RENDER START =======&apos;)
    const [pokemons, setPokemons] = useState([]);
    useEffect(async () =&gt; {
        const data = await fetch(`https://pokeapi.co/api/v2/pokemon?limit=10`).then(response =&gt; response.json());
        console.log(&apos;API Returned:&apos;, data.results);

        setPokemons(data.results);
        console.log(&apos;State right after setPokemon: &apos;, pokemons);
    }, [])
    
    console.log(&apos;State right before render: &apos;, pokemons);
    return &lt;ul&gt;{pokemons.map(pokemon =&gt; &lt;li key={pokemon.name}&gt;{pokemon.name}&lt;/li&gt;)}&lt;/ul&gt;
}</code></pre><p>Pause for a second - can you guess what the order of the <code>console.log</code>s is and what they will output?</p><p>...</p><p>...</p><p>Here&apos;s what the component will log:</p><pre><code class="language-text">======= RENDER START =======
State right before render:  []
API Returned: (10)&#xA0;[{&#x2026;}, {&#x2026;}, {&#x2026;}, {&#x2026;}, {&#x2026;}, {&#x2026;}, {&#x2026;}, {&#x2026;}, {&#x2026;}, {&#x2026;}]
======= RENDER START =======
State right before render:  (10)&#xA0;[{&#x2026;}, {&#x2026;}, {&#x2026;}, {&#x2026;}, {&#x2026;}, {&#x2026;}, {&#x2026;}, {&#x2026;}, {&#x2026;}, {&#x2026;}]
State right after setPokemon:  []
</code></pre><p>So what&apos;s going on?</p><p>RENDER 1</p><ul><li><code>state</code> is <code>[]</code></li><li>a new function is created to be passed into <code>useEffect</code></li><li>--&gt; function creates a closer over the current value of <code>state</code>: <code>[]</code></li><li>effect is called</li><li>--&gt; API is called to load the data</li><li>--&gt; data is successfully retrieved</li><li>--&gt; <code>setState</code> is called to update the pokemon list</li><li>--&gt; --&gt; RENDER 2 starts</li><li>--&gt; <code>console.log</code> statement is called logging <code>state</code> value wrapped in closure - <code>[]</code> -&gt; the old one!</li></ul><p>RENDER 2</p><ul><li><code>state</code> is now the list of pokemons</li><li>a new function is created to be passed into <code>useEffect</code></li><li>--&gt; function creates a closure over the current value of <code>state</code>: the full list of pokemons</li><li>effect is on longer called, since it was defined with empty deps array`, which means it only gets run on the first render</li></ul><p>So the <code>console.log</code> that outputs the empty array is actually a stale value, captured in the first render!</p><p></p><p>To make sure you really understand the flow, try drawing a diagram of the flow above with pen and paper!</p><p>I hope you found this helpful! Let me know in the comments below if you still have any questions &#x1F64F;</p>]]></content:encoded></item><item><title><![CDATA[Using yarn symlinks to share code between apps]]></title><description><![CDATA[<p>If you&apos;re working on multiple apps and you want to share code between them - for example common utils, data types or UI components - there are several options available.</p><p>I was recently browsing a <a href="https://www.reddit.com/r/typescript/comments/kro1pd/best_way_to_share_typescode_between_apps/?ref=jsramblings.com">reddit thread about monorepos and sharing code between multiple apps</a> and someone mentioned:</p>]]></description><link>https://jsramblings.com/using-yarn-symlinks-to-share-code-between-apps/</link><guid isPermaLink="false">64ecfadfa81cfd3240134827</guid><dc:creator><![CDATA[Corina]]></dc:creator><pubDate>Sun, 24 Jan 2021 21:45:26 GMT</pubDate><content:encoded><![CDATA[<p>If you&apos;re working on multiple apps and you want to share code between them - for example common utils, data types or UI components - there are several options available.</p><p>I was recently browsing a <a href="https://www.reddit.com/r/typescript/comments/kro1pd/best_way_to_share_typescode_between_apps/?ref=jsramblings.com">reddit thread about monorepos and sharing code between multiple apps</a> and someone mentioned:</p><blockquote>In my experience a monorepo with code symlinked at the <code>src</code> level is the best. The other options are a pain in the ass</blockquote><p>And I wondered - what does &quot;monorepo with code symlinked at the <code>src</code> level&quot; even mean? And how would you go about setting up something like this?</p><p>A bit of browsing through the <code>yarn</code> docs and I <a href="https://classic.yarnpkg.com/en/docs/cli/add/?ref=jsramblings.com">stumbled</a> on this piece:</p><blockquote><code>yarn add link:/path/to/local/folder</code> installs a symlink to a package that is on your local file system. This is useful to develop related packages in monorepo environments.</blockquote><p>So then a monorepo setup using yarn symlinks would simply consist of several libraries and apps, stored as siblings in one main repository, which reference each other through yarn local dependencies.</p><p>This can be a great way to share code if:</p><ul><li>you&apos;re looking for a simple solution to share code</li><li>you usually release your apps together (i.e. frontend + backend that share a library)</li></ul><p>This might not be the best option if:</p><ul><li>you want to release and publish each package / app independently</li><li>you want to be able to lock the shared package to a specific version, if needed</li></ul><p>If you think this might be a good solution for you, read along for a step by step walkthrough of how to set this up.</p><h2 id="folder-structure">Folder structure</h2><p>For the sake of this example, we&apos;ll use two projects - <code>project-a</code> and <code>project-b</code> - and a library that I named very bluntly <code>shared-code</code> &#x1F603;</p><p>All the code is <a href="https://gitlab.com/jsramblings/monorepo-1/-/tree/yarn-symlinks?ref=jsramblings.com">available on Gitlab</a>, if you want to check it out yourself.</p><p>Our monorepo will simply have all of these as siblings at the root level:</p><pre><code class="language-jsx">- shared-code
  - package.json
  - yarn.lock
- project-a
  - package.json
  - yarn.lock
- project-b
  - package.json
  - yarn.lock
</code></pre><p>Notice how each app / library is completely independent and has its own <code>package.json</code> and <code>yarn.lock</code> file. This is good because it keeps each project simple and fully contained.</p><h2 id="adding-dependencies">Adding dependencies</h2><p>To make <code>shared-code</code> available in both <code>project-a</code> and <code>project-b</code> you can simply run <code>yarn add link:/path</code> for each:</p><pre><code class="language-jsx">cd project-a
yarn add link:../shared-code
cd ..
cd project-b
yarn add link:../shared-code
</code></pre><p>This will add <code>shared-code</code> as a dependency in the <code>package.json</code> file:</p><figure class="kg-card kg-image-card"><img src="https://jsramblings.com/content/images/2021/01/image-3.png" class="kg-image" alt loading="lazy" width="2000" height="485" srcset="https://jsramblings.com/content/images/size/w600/2021/01/image-3.png 600w, https://jsramblings.com/content/images/size/w1000/2021/01/image-3.png 1000w, https://jsramblings.com/content/images/size/w1600/2021/01/image-3.png 1600w, https://jsramblings.com/content/images/size/w2400/2021/01/image-3.png 2400w" sizes="(min-width: 720px) 720px"></figure><h2 id="local-development-flow">Local development flow</h2><p>When something is changed in the <code>shared-code</code> library, does it get automatically refreshed or do you need to always rebuild the package?</p><p>The short answer is - it depends.</p><p>If you use Create React App, <a href="https://github.com/facebook/create-react-app/issues/1333?ref=jsramblings.com">this won&apos;t work</a>. First, the app expects the <code>shared-code</code> library to already be built, and second, the default CRA Webpack config does not detect changes in the <code>shared-code</code>, as it&apos;s outside of its <code>src</code> folder.</p><p>If you use your own custom setup, you can ensure hot reload by customizing your webpack config to detect changes in the symlinked packages. This is where having all the code under <code>src</code> might come in handy.</p><h2 id="continuous-integration">Continuous integration</h2><p>How would this work in CI?</p><p>Since all the code is in the same repository, the library will always be available to <code>yarn</code> on <code>yarn install</code>. However, <code>yarn</code> assumes the <code>shared-code</code> is already built, so will need to manually do this step when building any of the projects:</p><pre><code class="language-jsx"># Sample CI build script
# 1. First build the `shared-code`
cd shared-code
yarn install
yarn build
cd ..
# 2. Build the project
yarn install # Will symlink `shared-code` to the current `node_modules`
yarn build
</code></pre><p>That&apos;s it!</p><h2 id="caveats">Caveats</h2><p>This might make for a simple way to setup code sharing and a seamless developer experience, but there are several downsides to be aware of.</p><p>The most important one is related to the fact that both <code>project-a</code> and <code>project-b</code> always use just one version of the <code>shared-code</code> - the latest one available at a given point in time.</p><p>Since the <code>shared-code</code> is not published to an <code>npm</code> repository it&apos;s not possible to lock in a certain version. This means that there&apos;s a high chance that a change needed for <code>project-a</code> might break <code>project-b</code> unexpectedly.</p><h2 id="where-to-go-from-here">Where to go from here</h2><p>While this setup is good to get started, with time you might &quot;grow&quot; out of it.</p><p>If you notice a lot of your dependencies are duplicated in each of the apps / packages, and you&apos;d like to hoist them to the root level, you might want to look into using <a href="https://classic.yarnpkg.com/en/docs/workspaces/?ref=jsramblings.com">Yarn Workspaces</a>.</p><p>If you decide you want to publish the packages to an <code>npm</code> repository and want better versioning and publishing support, you might want to look at <a href="https://lerna.js.org/?ref=jsramblings.com">Lerna</a>.</p><p>I hope you found this useful! Let me know in the comments below if you have any questions or thoughts!</p>]]></content:encoded></item><item><title><![CDATA[Continuously deploy a React App to GitLab Pages]]></title><description><![CDATA[<p></p><p>If you&apos;re looking for a simple way to deploy a CRA app everytime you push new changes, Gitlab Pages can be a great way to do this.</p><p>By the end of this article you&apos;ll learn how to:</p><ul><li>setup a GitLab CI pipeline to build the app</li></ul>]]></description><link>https://jsramblings.com/continuously-deploy-a-react-app-to-gitlab-pages/</link><guid isPermaLink="false">64ecfadfa81cfd3240134826</guid><category><![CDATA[GitLab]]></category><dc:creator><![CDATA[Corina]]></dc:creator><pubDate>Sun, 17 Jan 2021 18:16:32 GMT</pubDate><content:encoded><![CDATA[<p></p><p>If you&apos;re looking for a simple way to deploy a CRA app everytime you push new changes, Gitlab Pages can be a great way to do this.</p><p>By the end of this article you&apos;ll learn how to:</p><ul><li>setup a GitLab CI pipeline to build the app on every new commit</li><li>deploy the app to GitLab Pages on every commit to <code>master</code>, automatically</li></ul><p>This walkthrough assumes you&apos;re using Gitlab to host your repository.</p><h2 id="step-1-setup-the-ci-pipeline-to-build-the-project">Step 1: Setup the CI pipeline to build the project</h2><p>What we want is to run <code>yarn build</code> after every commit and to save the contents of the <code>build</code> folder as a GitLab artifact that we can later use.</p><p>If you haven&apos;t already, create a <code>.gitlab-ci.yml</code> file in the root of your project:</p><pre><code>stages:
  - build

build:
  image: node:latest    # Run the job in a `node` docker image
  stage: build
  script:
    - yarn install      # Run `yarn install` and `yarn build`
    - yarn build
  artifacts:
    paths:
      - build/          # Save the build result as an artfact</code></pre><p>After pushing your changes, you should already see the pipeline running under &#xA0;the &quot;CI / CD &gt;&gt; Pipelines&quot; menu.</p><h2 id="step-2-configure-the-react-app-to-be-served-from-a-subfolder">Step 2: Configure the React app to be served from a subfolder</h2><p>By default, CRA apps expect the app will be served from the root of a server. </p><p>However, in GitLab Pages the app will be served from a subfolder (which represents the project name), and this will cause the React app to break and show the dreaded white page.</p><p>The fix is to customize the base path of the React app. There are two ways to configure this, depending on whether you use routing or not (or <a href="https://create-react-app.dev/docs/deployment/?ref=jsramblings.com#serving-the-same-build-from-different-paths">to be more specific</a>: &quot;if you are using the HTML5 <code>pushState</code> history API or using client-side routing&quot;).</p><h3 id="if-you-don-t-use-routes">If you don&apos;t use routes</h3><p>The easiest way to set this up if you don&apos;t use routes is to set the <code>homepage</code> attribute in your <code>package.json</code> to the current folder - <code>.</code> (see <a href="https://create-react-app.dev/docs/deployment/?ref=jsramblings.com#serving-the-same-build-from-different-paths">official docs</a>).</p><figure class="kg-card kg-image-card"><img src="https://jsramblings.com/content/images/2021/01/image-1.png" class="kg-image" alt loading="lazy" width="2000" height="377" srcset="https://jsramblings.com/content/images/size/w600/2021/01/image-1.png 600w, https://jsramblings.com/content/images/size/w1000/2021/01/image-1.png 1000w, https://jsramblings.com/content/images/size/w1600/2021/01/image-1.png 1600w, https://jsramblings.com/content/images/2021/01/image-1.png 2166w" sizes="(min-width: 720px) 720px"></figure><h3 id="if-you-use-routing">If you use routing</h3><p>Then you will need to hardcode the full URL. I recommend configuring the URL by passing in an environment variable at build time, not to pollute your <code>package.json</code> with paths.</p><p>First, you will need to figure out the URL of your Gitlab Pages site.<br>You can find it in the sidebar menu, under &quot;Settings &gt;&gt; Pages&quot;.</p><p>For example, for my example app, the project URL is <a href="https://gitlab.com/jsramblings/coffee-corner?ref=jsramblings.com">https://gitlab.com/jsramblings/coffee-corner</a> and the corresponding Gitlab Pages URL is <a href="https://jsramblings.gitlab.io/coffee-corner/?ref=jsramblings.com">https://jsramblings.gitlab.io/coffee-corner/</a>.</p><p>To pass the URL as an environment variable, we can pass the extra <code>variables</code> key to the <code>build</code> job:</p><pre><code>stages:
  - build

build:
  image: node:latest    
  stage: build
  variables: 
    # Replace this with your site URL
    PUBLIC_URL: https://jsramblings.gitlab.io/coffee-corner/  
  script:
    - yarn install      
    - yarn build
  artifacts:
    paths:
      - build/          </code></pre><h2 id="step-3-setup-the-gitlab-pages-deploy">Step 3: Setup the GitLab Pages deploy</h2><p>In order to have the site deployed automatically on every change to the code, we will add a new job to the CI pipeline.</p><p>Gitlab Pages <a href="https://docs.gitlab.com/ee/ci/yaml/README.html?ref=jsramblings.com#pages">expects two things</a> from the deploy job:</p><ul><li>it must be named <code>pages</code></li><li>it must publish its artifacts under a folder named <code>public</code></li></ul><pre><code># Stages run sequentially, so we add a new `deploy` stage 
# after the `build` one
stages:
  - build
  - deploy

build:
  ... # Same as before
  
pages:
  image: alpine:latest
  stage: deploy
  variables:
    GIT_STRATEGY: none        # Do not clone git repo
  script:
    # Rename the CRA `build` folder to `public`
    - mv build public         
  artifacts:
    paths:
      - public
</code></pre><p>That&apos;s it! Pushing your changes should already start deployting the app.</p><p>You&apos;ll see the two jobs - <code>build</code> and <code>pages</code>- in the Pipeline view, plus an extra <code>pages:deploy</code> job that confirms the site was published.</p><figure class="kg-card kg-image-card"><img src="https://jsramblings.com/content/images/2021/01/image-2.png" class="kg-image" alt loading="lazy" width="1118" height="508" srcset="https://jsramblings.com/content/images/size/w600/2021/01/image-2.png 600w, https://jsramblings.com/content/images/size/w1000/2021/01/image-2.png 1000w, https://jsramblings.com/content/images/2021/01/image-2.png 1118w" sizes="(min-width: 720px) 720px"></figure><p>I hope you found this walkthrough useful, let me know in the comments below if you have any questions &#x1F64F;</p>]]></content:encoded></item><item><title><![CDATA[Three ways to reuse commands across Gitlab jobs]]></title><description><![CDATA[<p>As soon as your pipeline grows a little bit beyond basic, it&apos;s common to get into a situation where a lot of the commands you run in each job are the same.</p><p>So how can you reduce this duplication? And should you?</p><p>In general, it&apos;s ok</p>]]></description><link>https://jsramblings.com/three-ways-to-reuse-commands-across-gitlab-jobs/</link><guid isPermaLink="false">64ecfadfa81cfd3240134825</guid><category><![CDATA[GitLab]]></category><dc:creator><![CDATA[Corina]]></dc:creator><pubDate>Wed, 14 Oct 2020 08:55:23 GMT</pubDate><content:encoded><![CDATA[<p>As soon as your pipeline grows a little bit beyond basic, it&apos;s common to get into a situation where a lot of the commands you run in each job are the same.</p><p>So how can you reduce this duplication? And should you?</p><p>In general, it&apos;s ok to have a bit of duplication, as it increases readability, but as the pipeline grows, that might not be so feasible anymore.</p><p>Let&apos;s take a look at 3 possible strategies using a simple example where we&apos;ll extract the <code>echo &apos;prepare&apos;</code> command, so it&apos;s no longer duplicated in every job.</p><pre><code class="language-yml">build:
  script:
    - echo &apos;prepare&apos;
    - echo &apos;build&apos;

test:
  script:
    - echo &apos;prepare&apos;
    - echo &apos;test&apos;
</code></pre><h2 id="use-a-default-before_script">Use a default <code>before_script</code></h2><p>If your command is something you want to run before all jobs, all the time, then the easiest way is to simply extract the common code as a <code>before_script</code> key at the beginning of the file:</p><pre><code class="language-yml">before_script:
  - echo &apos;prepare&apos;

build:
  script:
    - echo &apos;build&apos;

test:
  script:
    - echo &apos;test&apos;
</code></pre><p>While this does not give you a lot of flexibility, it&apos;s a very quick and straightforward way to reuse commands.</p><h2 id="use-yml-anchors">Use YML anchors</h2><p>Anchors are YML&apos;s way of reusing code - you can think of them a little bit like functions.</p><p>You can define a block of configuration somewhere and create a reference to it using <code>&amp;</code>. Then, you can use it with <code>*</code>.</p><p>Let&apos;s see an example:</p><pre><code class="language-yml"># Create an anchor called `&amp;prepare_step`
.prepare_step: &amp;prepare_step
  - echo &apos;prepare&apos;

build:
  script:
   # Merge the anchor into the `script` using `&lt;&lt;*:`
    - &lt;&lt;:*prepare_step
    - echo &apos;build&apos;

test:
  script:
   # Aaand reusing the command here again
    - &lt;&lt;:*prepare_step
    - echo &apos;test&apos;
</code></pre><p>You&apos;ll notice <code>prepare_step</code> job starts with a dot - this is intentional to prevent Gitlab from running that job; by starting it with a dot, it will be ignored, thus behaving like a &quot;template&quot;.</p><p>Anchors can be tricky to master, so if you want to learn more I found <a href="https://support.atlassian.com/bitbucket-cloud/docs/yaml-anchors?ref=jsramblings.com">this article</a> to be helpful, and of course, the official <a href="https://docs.gitlab.com/ee/ci/yaml/?ref=jsramblings.com#anchors">Gitlab docs on anchors</a>.</p><h2 id="use-extends">Use <code>extends</code></h2><p>While anchors can be quick to get started with, they do have their downsides. The main one I&apos;ve encountered is that you can&apos;t use anchors to reuse code across several files - they only work within the same file.</p><p>If you want to reuse code across several files, then you can use the <code>extends</code> keyword. This works similar to inheritance - you define a &quot;template&quot; job that other jobs can then extend.</p><p>Some things to keep in mind regarding <code>extends</code>:</p><ul><li>if the key is a hash it will get merged</li><li>if the key is an array, it will get overridden</li></ul><pre><code class="language-yml">.prepare_step:
  before_script:
    - echo &apos;prepare&apos;

build:
  extends:
    - .prepare_step
  script:
    - echo &apos;build&apos;

test:
  extends:
    - .prepare_step
  script:
    - echo &apos;test&apos;
</code></pre><p>Here are the <a href="https://docs.gitlab.com/ee/ci/yaml/?ref=jsramblings.com#extends">Gitlab docs on <code>extends</code> keyword</a> and some <a href="https://docs.gitlab.com/ee/ci/yaml/?ref=jsramblings.com#merge-details">details on its merging strategy</a>.</p>]]></content:encoded></item><item><title><![CDATA[Strategies for migrating from GitHub to GitLab]]></title><description><![CDATA[<p>Ooh, that moment when you decide to move to GitLab &#x2764;&#xFE0F;</p><p>It&apos;s all great, until you realize there are all these loose ends you need to think about &#x1F648;</p><ul><li>will the git commit authors be kept correctly after the import?</li><li>can I migrate issues / pull requests ?</li><li>can</li></ul>]]></description><link>https://jsramblings.com/strategies-for-migrating-from-github-to-gitlab/</link><guid isPermaLink="false">64ecfadfa81cfd3240134824</guid><category><![CDATA[GitLab]]></category><dc:creator><![CDATA[Corina]]></dc:creator><pubDate>Sun, 05 Apr 2020 07:03:05 GMT</pubDate><content:encoded><![CDATA[<p>Ooh, that moment when you decide to move to GitLab &#x2764;&#xFE0F;</p><p>It&apos;s all great, until you realize there are all these loose ends you need to think about &#x1F648;</p><ul><li>will the git commit authors be kept correctly after the import?</li><li>can I migrate issues / pull requests ?</li><li>can I just stop the existing CI pipeline or do other teams / projects depend on it?</li></ul><p>These are some of the things I&apos;ve had to think about while recently migrating the project I am working on to GitLab.</p><p>Here&apos;s what I learned.</p><p>I found there are 3 approaches to the migration.</p><h2 id="-1-migrate-everything-at-once">#1. Migrate everything at once</h2><p>This means you would:</p><ul><li>Start using the GitLab as hosted git repository instead of GitHub</li><li>Use GitLab as CI for your project</li><li>The previous GitHub project is closed</li></ul><p>This is the most straightforward way to go and should be your first choice if possible &#x1F680;.</p><h2 id="-2-migrate-your-ci-pipeline-first">#2. Migrate your CI pipeline first</h2><p>This means you would:</p><ul><li>Keep using GitHub as your hosted git repo</li><li>Use GitLab as CI for your GitHub repo</li></ul><p>Why would you want to do this?</p><ul><li>If there are external dependencies on your GitHub repo - e.g. Jenkins pipelines owned by other teams that can&apos;t/won&apos;t update them immediately</li><li>If you want to run both your &quot;old&quot; CI pipeline and the Gitlab one in parallel for a while, to make sure there are no regressions in how the artifacts are built</li><li>If you want to allow users to get used gradually to the GitLab ecosystem (i.e. not disrupt day to day work by switching cold turkey)</li></ul><p>This might sound a bit like overkill, but for a bigger organisation with multiple teams and a more complex release process, this could make sense.</p><p>Once the other teams have also switched to GitLab, you can make the full switch and no longer use GitHub at all.</p><h2 id="-3-migrate-fully-but-keep-github-as-a-read-only-mirror">#3. Migrate fully but keep GitHub as a read-only mirror</h2><p>This means you would:</p><ul><li>Use GitLab as the hosted git repository </li><li>Use GitLab as CI </li><li>Keep the GitHub repo, but make it read-only</li></ul><p>Why would you want to do this? </p><ul><li>If you have an open source project and you want to keep the visibility that GitHub brings </li><li>If you want to migrate 100%, but still have external dependencies (as described in step #2) -&gt; this option would bring the best of both worlds &#x1F4AA;</li></ul><p>This is the option I wish I had thought of when I migrated my project &#x1F600; I was too much &quot;in the middle of it&quot; that I only realised this could be an approach after I finished the migration with option #2.</p><h2 id="what-worked-for-you">What worked for you?</h2><p> Did you go through a migration from GitHub to GitLab? How did you approach it?</p><p>Here are some things I learned while doing this:</p><ul><li><a href="https://jsramblings.com/keep-authors-when-importing-from-github-to-gitlab/">Steps for correctly importing the commit authors</a></li><li><a href="https://jsramblings.com/automatically-sync-gitlab-mirrored-repository-on-every-push-to-github/">Steps to get GitHub changes synced to the GitLab repo in real-time</a></li></ul><p></p>]]></content:encoded></item></channel></rss>