<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: mihomihouk</title>
    <description>The latest articles on DEV Community by mihomihouk (@mihomihouk).</description>
    <link>https://dev.to/mihomihouk</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F834295%2F59d7de80-e404-4c6d-8b85-5bab057192c2.JPG</url>
      <title>DEV Community: mihomihouk</title>
      <link>https://dev.to/mihomihouk</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mihomihouk"/>
    <language>en</language>
    <item>
      <title>How to be a web accessibility practitioner in two days</title>
      <dc:creator>mihomihouk</dc:creator>
      <pubDate>Sun, 30 Jul 2023 23:17:28 +0000</pubDate>
      <link>https://dev.to/mihomihouk/how-to-be-a-web-accessibility-practitioner-in-two-days-478k</link>
      <guid>https://dev.to/mihomihouk/how-to-be-a-web-accessibility-practitioner-in-two-days-478k</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;I first learned about accessibility from a senior engineer when I was looking for a new role as a Junior software engineer. He told me that while more and more companies are interested in accessibility, not many people have hands-on experience let alone learn about it themselves, so adding it to my skill set would make me stand out. &lt;/p&gt;

&lt;p&gt;As this story implies, accessibility is currently considered as a "nice-to-have skill" rather than compulsory. &lt;a href="https://webaim.org/projects/million/#conclusion" rel="noopener noreferrer"&gt;Recent research &lt;/a&gt;that evaluated 1 million websites shows that an average of 50 errors per page was detected and 96.3% of homepages failed to meet WCAG 2 standards. But the more I learn about it, the stronger I came to feel that accessibility is an &lt;em&gt;essential process&lt;/em&gt; that every software development should include in its life cycle.&lt;/p&gt;

&lt;p&gt;In this article, I will share how I learned and improved the accessibility of my project within just two days without prior knowledge.&lt;/p&gt;

&lt;h2&gt;
  
  
  Day 1: Learn about accessibility
&lt;/h2&gt;

&lt;p&gt;Just like learning new languages and frameworks, accessibility requires some research and learning. So I dedicated one day to learning about the background and methods of accessibility.&lt;/p&gt;

&lt;h3&gt;
  
  
  Understand the background
&lt;/h3&gt;

&lt;p&gt;The goals here are to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Understand why accessibility is important.&lt;/li&gt;
&lt;li&gt;Understand how people with disabilities are experiencing inaccessible websites/apps.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To achieve this, I used different resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Videos on Youtube (e.g., &lt;a href="https://www.youtube.com/watch?v=-ao_Kc_8rpE&amp;amp;t=624s" rel="noopener noreferrer"&gt;Accessible Web Design: What Is It &amp;amp; How To Do It&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Podcast (e.g., &lt;a href="https://topenddevs.com/podcasts/javascript-jabber/episodes/jsj-362-accessibility-with-chris-demars" rel="noopener noreferrer"&gt;Javascript Jabber 362: Accessibility with Chris DeMars&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Blog article (e.g.,&lt;a href="https://www.technologyreview.com/2022/04/07/1048543/despite-efforts-businesses-struggle-with-accessibility/" rel="noopener noreferrer"&gt;Despite efforts, businesses struggle with accessibility&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Keeping the goals in mind, I watched/read/listened to these materials just to get an overview and extract necessary information. &lt;/p&gt;

&lt;p&gt;While it's ultimately up to you, I wouldn't recommend using &lt;a href="https://www.w3.org/TR/WCAG21/#background-on-wcag-2" rel="noopener noreferrer"&gt;WCAG 2.1 document&lt;/a&gt; just yet as the volume might be overwhelming and it can easily put you off if you are still new to this area. &lt;/p&gt;

&lt;p&gt;Below are the examples of what I learned from this learning:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;There are many types of disabilities(e.g.,visual impairment, deafness and hearing loss, limited movement, speech disabilities, photosensitivity).&lt;/li&gt;
&lt;li&gt;Disability can be temporal (e.g., weakened eyesight with ageing or sudden injury).&lt;/li&gt;
&lt;li&gt;Disability is situational (= certain bodily and cognitive characters become "disability" due to the environment surrounding them).&lt;/li&gt;
&lt;li&gt;There are three different accessibility levels: AAA(highest level), AA(a level that most development teams should be aiming to meet) and A.&lt;/li&gt;
&lt;li&gt;Currently, the UK law asks companies to make a "reasonable adjustment" to make their website/applications accessible to people with disabilities (&lt;a href="https://www.legislation.gov.uk/ukpga/2010/15/contents" rel="noopener noreferrer"&gt;Section 20 and 29 of Equality Act 2010&lt;/a&gt;). &lt;/li&gt;
&lt;li&gt;There are different views towards accessibility (time constraints? cost? good branding? moral responsibility?)&lt;/li&gt;
&lt;li&gt;Accessible website = SEO friendly&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Understand the accessibility level of our website/application
&lt;/h3&gt;

&lt;p&gt;After learning about the context of accessibility, I checked my current personal project to see how accessible it is. First I generated a Lighthouse report.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.chrome.com/docs/lighthouse/overview/" rel="noopener noreferrer"&gt;Lighthouse&lt;/a&gt; is an amazing open-source Google Chrome tool that helps us improve the quality of our website/application. To use it, you access the page you want to test in your Chrome browser and find Lighthouse in the Dev tool. After selecting the accessibility option and running the audit, you will see a report that shows the accessibility level of the page and the issues that should be addressed to improve accessibility. &lt;/p&gt;

&lt;p&gt;In my application, for example, I got a warning about the lack of names for some buttons.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyrn1qw3rqxs77p3b9cko.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyrn1qw3rqxs77p3b9cko.png" alt="Lighthouse report example" width="800" height="433"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And then, I navigated the deployed website with assistive technologies to gain a better understanding of how my application would be experienced by people with disabilities.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keyboard (Tab key to move up and down) &lt;/li&gt;
&lt;li&gt;Screen reader (For those with visual impairment. As a Mac user, I used &lt;a href="https://support.apple.com/en-gb/guide/voiceover/welcome/mac" rel="noopener noreferrer"&gt;Voice over&lt;/a&gt;.)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://michelf.ca/projects/sim-daltonism/" rel="noopener noreferrer"&gt;Sim Daltonism&lt;/a&gt; (For colour blindness.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sim Daltonism is an amazing tool. You can use the tool just like a filter through which you can see screens through the eyes of people with colour blindness. &lt;/p&gt;

&lt;p&gt;As you can see, the text "Log in" inside the red circle is almost invisible due to poor colour contrast. This is only noticeable because I am using Sim Daltonism's filter here.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F11hogt63bqupqdo8j6gl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F11hogt63bqupqdo8j6gl.png" alt="Inaccessible colour contrast in my application" width="800" height="470"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Learn the solutions and make your Accessible checklist
&lt;/h3&gt;

&lt;p&gt;I learned about potential audiences I could reach out to by improving accessibility and accessibility concerns of my current application. So in this third step, I focused on learning how to solve these problems. &lt;/p&gt;

&lt;h4&gt;
  
  
  Learn from real-world examples of accessibility
&lt;/h4&gt;

&lt;p&gt;Just as I did in my first step, I used Youtube videos and articles to educate myself. In this process, I spent more time learning from real-world front-end development examples. I highly recommend reading &lt;a href="https://octopus.energy/blog/web-accessibility-at-octopus/" rel="noopener noreferrer"&gt;what Octopus Energy has been doing on its website and application for its users&lt;/a&gt;. This company is very serious about accessibility and has actively documented its accessibility journey to make accessibility implementable for developers like you and me!&lt;/p&gt;

&lt;h4&gt;
  
  
  Iterate your accessible checklist
&lt;/h4&gt;

&lt;p&gt;To facilitate the future process, I made a checklist and added accessibility coding techniques I learned from videos and articles to it. Every website and application has different UI and functions, so it is important to make a customised list that suits your project.&lt;/p&gt;

&lt;p&gt;If you want to make it more comprehensive, &lt;a href="https://www.w3.org/WAI/WCAG21/quickref/" rel="noopener noreferrer"&gt;WCAG quick reference&lt;/a&gt; should give you a thorough list of points to cover.&lt;/p&gt;

&lt;h2&gt;
  
  
  Day 2: Apply accessibility to your project
&lt;/h2&gt;

&lt;p&gt;On my second day, I applied all my learnings to my current project. &lt;/p&gt;

&lt;h3&gt;
  
  
  Use tools as your guide
&lt;/h3&gt;

&lt;p&gt;I already touched Lighthouse earlier and I used the tool again to test the accessibility of every page in my application. All I needed to do is to read the report and make adjustments such as adding attributes and &lt;code&gt;tabIndex={0}&lt;/code&gt; to make UI readable by screen reader and accessible by keyboard and fixing semantic HTML. &lt;/p&gt;

&lt;p&gt;While Lighthouse undoubtedly facilitates web accessibility, it is not a panacea. For example, the image below shows the accessibility rate of the page is 100%. But as we looked at earlier, the colour contrast of this page is inaccessible to people with colour blindness. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnplejuxw6wuro9pvwx6q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnplejuxw6wuro9pvwx6q.png" alt="Landing page with Lighthouse 100% accessibility evaluation" width="800" height="460"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Other useful tools
&lt;/h3&gt;

&lt;p&gt;To compensate for Lighthouse reports, I used several other materials and conducted manual testing. Here I share some websites and browser extensions that I found useful to improve the accessibility of my web application.&lt;/p&gt;

&lt;h4&gt;
  
  
  Colour contrast
&lt;/h4&gt;

&lt;p&gt;To achieve AAA-level colour contrast, I used the following websites:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://webaim.org/resources/contrastchecker/" rel="noopener noreferrer"&gt;WebAIM&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://www.sussex.ac.uk/tel/resource/tel_website/accessiblecontrast/?q=FFFFFF~003b49~1d4289~94a596~d3273e~00bfb2~d6d2c4~ffc845~dc582a~41b6e6~1b365d~7da1c4~f2c75c~d0d3d4~007a78~000000" rel="noopener noreferrer"&gt;Accessible Colour Contrast&lt;/a&gt; &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;WebAIM is a contrast checker where we can add two colours in the inputs to see their contrast ratio and how well the ratio meets accessibility standards.&lt;/p&gt;

&lt;p&gt;Accessible Colour Contrast, on the other hand, shows you options for accessible colour combinations. You can filter the options depending on which accessibility level you are aiming at. Using this website, I chose the primary and secondary colours of my application. &lt;/p&gt;

&lt;p&gt;Let's have a look at my landing page which used to be very inaccessible. In the image with Sim Daltonism's filter below, we can clearly see the "Log in" text. This is a good example of how much difference we can make by just changing the colour contrast. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjwdxsuc2ipvmlq01b6nc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjwdxsuc2ipvmlq01b6nc.png" alt="Landing page of my application after accessibility improvement" width="800" height="430"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  HTML Outline
&lt;/h4&gt;

&lt;p&gt;Messy or flat HTML structures confuse screen readers and hinder them from navigating websites/applications efficiently.&lt;/p&gt;

&lt;p&gt;If you are a Google Chrome user, the following extensions surely help you find out whether the elements are properly organised.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://chrome.google.com/webstore/detail/html5-outliner/afoibpobokebhgfnknfndkgemglggomo" rel="noopener noreferrer"&gt;HTML5 Outliner&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://chrome.google.com/webstore/detail/headingsmap/flbjommegcjonpdmenkdiocclhjacmbi" rel="noopener noreferrer"&gt;HeadingsMap&lt;/a&gt; &lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Example: how my application changed after accessibility improvements
&lt;/h2&gt;

&lt;p&gt;After the implementation, I found my web application fully functioning with keyboard navigation. Screen reader now clearly tells what users can expect from each element. There is no keyboard trap and colour contrasts are all AAA level. &lt;/p&gt;

&lt;p&gt;Below shows how this implementation changed my application visually.&lt;/p&gt;

&lt;p&gt;Before(sign up page):&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6voqt4pu8h6qzc2x9r66.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6voqt4pu8h6qzc2x9r66.png" alt="The sign up page before accessibility improvement" width="800" height="582"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After(sign up page):&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frt8taa3kwy594iuo9y2s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frt8taa3kwy594iuo9y2s.png" alt="The sign up page before accessibility improvement" width="800" height="607"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Before(modal):&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4rfrlfkmwelrve7tz9fl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4rfrlfkmwelrve7tz9fl.png" alt="A modal before accessibility improvement" width="800" height="459"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After(modal):&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcuamwlir1ltmv06hwi2q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcuamwlir1ltmv06hwi2q.png" alt="A modal after accessibility improvement" width="800" height="620"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The landing page now looks a bit empty, but this is just the first iteration. Accessible websites and applications don't need to be boring.&lt;/p&gt;

&lt;p&gt;But I was personally surprised to learn how quickly I could achieve this level of accessibility. And I'm sure it will only become easier as I experience more. &lt;/p&gt;

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

&lt;p&gt;Ultimately, as Ashley Firth suggests in &lt;a href="https://learna11y.com/" rel="noopener noreferrer"&gt;&lt;em&gt;Practical Web Inclusion and Accessibility&lt;/em&gt;&lt;/a&gt;, accessibility should be a collaborative effort if we are part of collaborative projects. This means even if we are passionate about accessibility and include it in the code we write, if others are not on board, your websites/applications stay largely inaccessible to people with disabilities. &lt;/p&gt;

&lt;p&gt;But it does not mean we have to wait until our boss asks us to start learning it. If we believe it's the right thing to do, we should keep learning it, improving our skills and sharing our experience with our colleagues. &lt;/p&gt;

&lt;p&gt;To stay involved in web accessibility, we could either:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Find like-minded people to start a new project with accessibility practices.&lt;/li&gt;
&lt;li&gt;Become an advocate to drive accessibility implementation in our organisations.&lt;/li&gt;
&lt;li&gt;Join a company which has been already committed to accessibility. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;

&lt;p&gt;Please let me know your web accessibility experience and the tools you use to improve the accessibility of your websites and applications.&lt;/p&gt;

</description>
      <category>a11y</category>
      <category>html</category>
      <category>testing</category>
      <category>discuss</category>
    </item>
    <item>
      <title>How to make a reusable button component with Typescript in React applications</title>
      <dc:creator>mihomihouk</dc:creator>
      <pubDate>Fri, 21 Jul 2023 17:52:15 +0000</pubDate>
      <link>https://dev.to/mihomihouk/how-to-make-a-reusable-button-component-with-typescript-in-react-applications-2047</link>
      <guid>https://dev.to/mihomihouk/how-to-make-a-reusable-button-component-with-typescript-in-react-applications-2047</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Do you find your reusable component getting messier as your project develops? If so, it might be a good time to start refactoring it!&lt;/p&gt;

&lt;p&gt;In this article, I will share one way to quickly improve the readability and maintainability of a reusable component using a button component as an example.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cluttered button component
&lt;/h2&gt;

&lt;p&gt;Here we have a reusable, but very cluttered button component.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Link, LinkProps } from 'react-router-dom';
import React, { CSSProperties } from 'react';
import classNames from 'classnames';
import { FadeLoader } from 'react-spinners';

const spinnerOverride: CSSProperties = {
  margin: '0 auto',
  top: '30px'
};

interface ButtonProps {
  className?: string;
  onClick?: () =&amp;gt; void;
  to?: LinkProps['to'];
  children?: React.ReactNode;
  inNav?: boolean;
  isPrimary?: boolean;
  isSecondary?: boolean;
  isWarning?: boolean;
  disabled?: boolean;
  isLoading?: boolean;
  type: 'button' | 'reset' | 'submit' | undefined;
  testId?: string;
}
export const Button: React.FC&amp;lt;ButtonProps&amp;gt; = ({
  className,
  onClick,
  to,
  inNav,
  isPrimary,
  isSecondary,
  isWarning,
  type,
  testId,
  disabled,
  isLoading,
  children
}) =&amp;gt; {
  const hrefTo = to ?? '#';
  if (isLoading) {
    return (
      &amp;lt;FadeLoader
        data-testid="loading"
        loading={isLoading}
        height={10}
        width={10}
        cssOverride={spinnerOverride}
        aria-label="Loading Spinner"
      /&amp;gt;
    );
  }

  if (to) {
    return (
      &amp;lt;Link
        className={classNames(
          {
            'pl-0 lg:pl-3 py-2 rounded-3xl hover:bg-gray-300 ease-in duration-300':
              inNav
          },
          {
            'w-full bg-primary-500 text-white inline-block p-2 rounded-lg hover:bg-opacity-75 focus:outline-none focus:ring focus:border-primary-800':
              isPrimary
          },
          {
            'w-full bg-secondary-500 text-white inline-block p-2 rounded-lg hover:bg-opacity-75 focus:outline-none focus:ring focus:border-secondary-800':
              isSecondary
          },
          {
            'w-full bg-error-500 text-white inline-block p-2 rounded-lg hover:bg-opacity-75 focus:outline-none focus:ring focus:border-error-800':
              isWarning
          },
          {
            'cursor-not-allowed': disabled
          },
          className
        )}
        to={hrefTo}
      &amp;gt;
        &amp;lt;button
          className={classNames(
            'w-full',
            { 'flex gap-4': inNav },
            {
              'cursor-not-allowed': disabled
            }
          )}
          data-testid={testId}
          tabIndex={0}
          onClick={onClick}
          disabled={disabled}
          type={type}
        &amp;gt;
          {children}
        &amp;lt;/button&amp;gt;
      &amp;lt;/Link&amp;gt;
    );
  }

  return (
    &amp;lt;button
      className={classNames(
        {
          'w-full bg-primary-500 text-white inline-block p-2 rounded-lg hover:bg-opacity-75':
            isPrimary
        },
        {
          'w-full bg-secondary-500 text-white inline-block p-2 rounded-lg hover:bg-opacity-75':
            isSecondary
        },
        {
          'w-full bg-error-500 text-white inline-block p-2 rounded-lg hover:bg-opacity-75':
            isWarning
        },
        {
          'py-2 lg:pl-3 rounded-3xl hover:bg-gray-300 ease-in duration-300 flex gap-4 w-full ':
            inNav
        },
        {
          'cursor-not-allowed': disabled
        },
        className
      )}
      data-testid={testId}
      tabIndex={0}
      onClick={onClick}
      disabled={disabled}
      type={type}
    &amp;gt;
      {children}
    &amp;lt;/button&amp;gt;
  );
};

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

&lt;/div&gt;



&lt;p&gt;As a project develops, it's common for a reusable component to start accepting more and more props to extend its versatility. Depending on where it's used, we might want to pass different styles, types, IDs for testing, accessibility attributes, and more to a component.&lt;/p&gt;

&lt;p&gt;But is it really necessary to explicitly write every single prop when we type, destructure, and consume them like this?&lt;/p&gt;

&lt;p&gt;Absolutely not!&lt;/p&gt;

&lt;h2&gt;
  
  
  Refactor a reusable component using JSX.IntrinsicElements
&lt;/h2&gt;

&lt;p&gt;One way to improve the readability and maintainability of a reusable component is to leverage React types.&lt;/p&gt;

&lt;p&gt;Let's perform some magic!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Link, LinkProps } from 'react-router-dom';
import React, { CSSProperties } from 'react';
import classNames from 'classnames';
import { FadeLoader } from 'react-spinners';

const spinnerOverride: CSSProperties = {
  margin: '0 auto',
  top: '30px'
};

type IntrinsicButtonProps = JSX.IntrinsicElements['button'];

interface ButtonProps extends IntrinsicButtonProps {
  to?: LinkProps['to'];
  inNav?: boolean;
  isPrimary?: boolean;
  isSecondary?: boolean;
  isWarning?: boolean;
  isLoading?: boolean;
}

export const Button: React.FC&amp;lt;ButtonProps&amp;gt; = ({
  className,
  to,
  inNav,
  isPrimary,
  isSecondary,
  isWarning,
  disabled,
  isLoading,
  children,
  ...props
}) =&amp;gt; {
  const hrefTo = to ?? '#';
  if (isLoading) {
    return (
      &amp;lt;FadeLoader
        data-testid="loading"
        loading={isLoading}
        height={10}
        width={10}
        cssOverride={spinnerOverride}
        aria-label="Loading Spinner"
      /&amp;gt;
    );
  }

  if (to) {
    return (
      &amp;lt;Link
        className={classNames(
          {
            'pl-0 lg:pl-3 py-2 rounded-3xl hover:bg-gray-300 ease-in duration-300':
              inNav
          },
          {
            'w-full bg-primary-500 text-white inline-block p-2 rounded-lg hover:bg-opacity-75 focus:outline-none focus:ring focus:border-primary-800':
              isPrimary
          },
          {
            'w-full bg-secondary-500 text-white inline-block p-2 rounded-lg hover:bg-opacity-75 focus:outline-none focus:ring focus:border-secondary-800':
              isSecondary
          },
          {
            'w-full bg-error-500 text-white inline-block p-2 rounded-lg hover:bg-opacity-75 focus:outline-none focus:ring focus:border-error-800':
              isWarning
          },
          {
            'cursor-not-allowed': disabled
          },
          className
        )}
        to={hrefTo}
      &amp;gt;
        &amp;lt;button
          className={classNames(
            'w-full',
            { 'flex gap-4': inNav },
            {
              'cursor-not-allowed': disabled
            }
          )}
          {...props}
        &amp;gt;
          {children}
        &amp;lt;/button&amp;gt;
      &amp;lt;/Link&amp;gt;
    );
  }

  return (
    &amp;lt;button
      className={classNames(
        {
          'w-full bg-primary-500 text-white inline-block p-2 rounded-lg hover:bg-opacity-75':
            isPrimary
        },
        {
          'w-full bg-secondary-500 text-white inline-block p-2 rounded-lg hover:bg-opacity-75':
            isSecondary
        },
        {
          'w-full bg-error-500 text-white inline-block p-2 rounded-lg hover:bg-opacity-75':
            isWarning
        },
        {
          'py-2 lg:pl-3 rounded-3xl hover:bg-gray-300 ease-in duration-300 flex gap-4 w-full ':
            inNav
        },
        {
          'cursor-not-allowed': disabled
        },
        className
      )}
      {...props}
    &amp;gt;
      {children}
    &amp;lt;/button&amp;gt;
  );
};

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

&lt;/div&gt;



&lt;p&gt;Wow! &lt;/p&gt;

&lt;p&gt;Can you see the difference from the previous code?&lt;br&gt;
The amount of code clearly reduced, making the component a lot easier to read.&lt;/p&gt;

&lt;p&gt;So what have been changed?&lt;/p&gt;
&lt;h3&gt;
  
  
  Change the type of the props
&lt;/h3&gt;

&lt;p&gt;The biggest difference is how we declare the type of the props.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;type IntrinsicButtonProps = JSX.IntrinsicElements['button'];
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First, this line of code extends &lt;code&gt;JSX.IntrinsicElements['button']&lt;/code&gt;, which includes all the standard attributes available for a  element.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
interface ButtonProps extends IntrinsicButtonProps {
  to?: LinkProps['to'];
  inNav?: boolean;
  isPrimary?: boolean;
  isSecondary?: boolean;
  isWarning?: boolean;
  isLoading?: boolean;
}

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

&lt;/div&gt;



&lt;p&gt;And then, we declare a new interface &lt;code&gt;ButtonProps&lt;/code&gt; by adding custom attributes to &lt;code&gt;IntrinsicButtonProps&lt;/code&gt;, which already has the basic button attributes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adjust the way to destructure and use props
&lt;/h3&gt;

&lt;p&gt;The second difference is the way we destructure and use the props.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const Button: React.FC&amp;lt;ButtonProps&amp;gt; = ({
  className,
  to,
  inNav,
  isPrimary,
  isSecondary,
  isWarning,
  disabled,
  isLoading,
  children,
  ...props
}) =&amp;gt; {
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;button
  className={classNames(
  'w-full',
  { 'flex gap-4': inNav },
  {
   'cursor-not-allowed': disabled
   }
  )}
  {...props}
&amp;gt;
 {children}
&amp;lt;/button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When destructing, we extract some items by explicitly writing each one of them while handling the rest by using spread operator.&lt;/p&gt;

&lt;p&gt;The extraction is useful for an item used conditionally as you can see in my example. We pass the rest to &lt;code&gt;button&lt;/code&gt; tag by again using spread operator.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Final remark
&lt;/h2&gt;

&lt;p&gt;I believe that enhancing the versatility of a reusable component necessities some refactoring.&lt;/p&gt;

&lt;p&gt;Using the &lt;code&gt;JSX.IntrinsicElements&lt;/code&gt; is one of the approach and we already see a huge improvement in code.&lt;/p&gt;

&lt;p&gt;What is your strategy to extend the versatility of a reusable component without compromising the readability and maintainability?&lt;/p&gt;

&lt;p&gt;I am keen to know!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>react</category>
      <category>typescript</category>
      <category>programming</category>
    </item>
    <item>
      <title>Testing React Portals: A Real-Life Example for testing a modal</title>
      <dc:creator>mihomihouk</dc:creator>
      <pubDate>Thu, 20 Jul 2023 17:16:53 +0000</pubDate>
      <link>https://dev.to/mihomihouk/test-a-portal-element-in-a-react-app-152h</link>
      <guid>https://dev.to/mihomihouk/test-a-portal-element-in-a-react-app-152h</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In React development, a portal is like a secret passage that lets you render components outside the usual DOM setup.&lt;/p&gt;

&lt;p&gt;In this article, we'll dive into portals and how to test it properly with Jest/react-testing-library with my real example.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is portal and when to use it
&lt;/h2&gt;

&lt;p&gt;A &lt;code&gt;portal&lt;/code&gt; is a mechanism that allows you to render components outside of the normal DOM hierarchy. &lt;/p&gt;

&lt;p&gt;Imagine you want to render components like modals, dialogs, and tooltips without letting them affected by the position or styling of their parent elements. Placing the component in a &lt;code&gt;portal&lt;/code&gt; works perfectly in this scenario while still allowing the component to consume and update context shared in the normal DOM tree.&lt;/p&gt;

&lt;p&gt;When modal is hidden:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frz4e2srn1w9pqo9qin7r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frz4e2srn1w9pqo9qin7r.png" alt="browser element tab" width="800" height="421"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When modal is visible:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqafqd6lq7wmm8nzbkui8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqafqd6lq7wmm8nzbkui8.png" alt="browser element tab" width="800" height="428"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, the modal rendered into a portal is added outside of &lt;code&gt;root&lt;/code&gt;!!&lt;/p&gt;

&lt;p&gt;If you want to explore more, visit &lt;a href="https://react.dev/reference/react-dom/createPortal" rel="noopener noreferrer"&gt;this page&lt;/a&gt; on React's website.&lt;/p&gt;

&lt;h2&gt;
  
  
  Custom modal as an example
&lt;/h2&gt;

&lt;p&gt;Before discussing testing a portal component, I'll share how I set up my modal component.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import classNames from 'classnames';
import React from 'react';
import ReactDOM from 'react-dom';

interface ModalProps {
  children?: React.ReactNode;
  onDismiss?: () =&amp;gt; void;
  className?: string;
}

export const Modal: React.FC&amp;lt;ModalProps&amp;gt; = ({
  children,
  onDismiss,
  className
}) =&amp;gt; {
  React.useEffect(() =&amp;gt; {
    const handleEscKey = (event: KeyboardEvent) =&amp;gt; {
      if (event.key === 'Escape') {
        onDismiss &amp;amp;&amp;amp; onDismiss();
      }
    };
    document.querySelector('body')?.classList.add('overflow-hidden');
    document.addEventListener('keydown', handleEscKey);

    return () =&amp;gt; {
      document.querySelector('body')?.classList.remove('overflow-hidden');
      document.removeEventListener('keydown', handleEscKey);
    };
  }, [onDismiss]);

  return ReactDOM.createPortal(
    &amp;lt;div
      className="flex flex-col items-center fixed bottom-0 right-0 left-0 top-0 z-[1000] bg-[#1e1e1e99]"
      onClick={() =&amp;gt; onDismiss?.()}
      data-testid="modal"
    &amp;gt;
      &amp;lt;div
        className={classNames(
          className,
          'sm:w-[600px] sm:h-[500px] md:w-[700px] md:h-[600px] absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 overflow-auto rounded-2xl bg-gray-800'
        )}
        onClick={(e) =&amp;gt; e.stopPropagation()}
      &amp;gt;
        {children}
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;,
    document.body
  );
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I used &lt;code&gt;createPortal&lt;/code&gt; API provided by React. Following the syntax &lt;code&gt;createPortal(children, domNode, key?)&lt;/code&gt;, I pass my modal component as the first parameter and &lt;code&gt;document.body&lt;/code&gt; as a place I want to render the modal to the API.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;App.tsx

import React from 'react';
import './index.css';
import { ModalsContainer } from './container/modals-container';
import { AppRoutes } from './routes/AppRoutes';

function App() {
  return (
    &amp;lt;&amp;gt;
      &amp;lt;AppRoutes /&amp;gt;
      &amp;lt;ModalsContainer /&amp;gt;
    &amp;lt;/&amp;gt;
  );
}

export default App;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And I placed a group of modals (&lt;code&gt;&amp;lt;ModalsContainer /&amp;gt;&lt;/code&gt;) as well as other components (&lt;code&gt;&amp;lt;AppRoutes /&amp;gt;&lt;/code&gt;) in &lt;code&gt;App&lt;/code&gt; component.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test modal
&lt;/h2&gt;

&lt;p&gt;Sorry to keep you waiting for so long. We can finally talk about how to test a portal.&lt;/p&gt;

&lt;p&gt;If you want to skip ahead, please go straight into my final solution. But if you are curious enough, let's look at my testing strategies and the mistake I made first.&lt;/p&gt;

&lt;h3&gt;
  
  
  What I wanted to test
&lt;/h3&gt;

&lt;p&gt;I wanted to verify that once a button is clicked, the modal opens properly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing strategy
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Update Redux state necessary for the test.&lt;/li&gt;
&lt;li&gt;Render &lt;code&gt;&amp;lt;App/&amp;gt;&lt;/code&gt; component and pass a particular route (in this case, a path to dashboard page).&lt;/li&gt;
&lt;li&gt;Ensure the page is rendered properly.&lt;/li&gt;
&lt;li&gt;Click a button that opens a modal.&lt;/li&gt;
&lt;li&gt;Ensure the expected modal is successfully rendered by checking specific content inside the modal.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Initial code (don't do this)
&lt;/h3&gt;

&lt;p&gt;To test the above step, I initially created the test code below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { renderWithProviders } from '../../util/test';
import '@testing-library/jest-dom';
import { screen, waitForElementToBeRemoved } from '@testing-library/react';
import { setupStore } from '../../store/store';
import { showModal } from '../../slices/modals-slice';
import { ModalType } from '../../interfaces/modal-type';
import App from '../../App';
import { setIsAuthenticated, setUser } from '../../slices/auth-slice';
import { mockUser } from '../../mocks/user';
import userEvent from '@testing-library/user-event';
import { server } from '../../mocks/server';
import { rest } from 'msw';
import config from '../../config';

const baseURL = config.apiUrl;

describe('Modal component', () =&amp;gt; {
  it('should render modal when prompted', async () =&amp;gt; {
    server.use(
      rest.get(`${baseURL}/post/get-posts`, (req, res, ctx) =&amp;gt; {
        return res(ctx.json([]));
      })
    );
    const store = setupStore();
    store.dispatch(setIsAuthenticated(true));
    store.dispatch(setUser(mockUser));
    renderWithProviders(&amp;lt;App /&amp;gt;, { store, initialRoutes: ['/dashboard'] });

    await waitForElementToBeRemoved(() =&amp;gt; screen.queryByTestId('main loader'));
    expect(
      await screen.findByText('Create a new post to get started.')
    ).toBeInTheDocument();

    userEvent.click(screen.getByTestId('create-post-btn'));

    expect(screen.getByText('Create new post')).toBeInTheDocument();

  });
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After running this test, I encountered the following error:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;TestingLibraryElementError: Unable to find an element with the text: Create new post. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Oh nooooo!&lt;/p&gt;

&lt;p&gt;I could ensure that all the testing steps were completed except for the last part: &lt;/p&gt;

&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;Ensure an expected modal is successfully rendered by getting a text inside the modal. &lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;

&lt;p&gt;React testing library fails to find a text inside the modal after clicking a button to open it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Final code (definitely try this!)
&lt;/h3&gt;

&lt;p&gt;Don't worry, this article has a happy ending.&lt;/p&gt;

&lt;p&gt;After searching for a workaround, I found &lt;a href="https://github.com/testing-library/react-testing-library/issues/62" rel="noopener noreferrer"&gt;this issue thread&lt;/a&gt; on the React-testing-library Github page, which gave me a clear solution.&lt;/p&gt;

&lt;p&gt;I applied it in my testing code...and ta-da!&lt;/p&gt;

&lt;p&gt;Now I could see my test pass successfully!&lt;/p&gt;

&lt;p&gt;Here is the testing code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { renderWithProviders } from '../../util/test';
import '@testing-library/jest-dom';
import {
  getQueriesForElement,
  screen,
  waitForElementToBeRemoved
} from '@testing-library/react';
import { setupStore } from '../../store/store';
import App from '../../App';
import { setIsAuthenticated, setUser } from '../../slices/auth-slice';
import { mockUser } from '../../mocks/user';
import userEvent from '@testing-library/user-event';
import { server } from '../../mocks/server';
import { rest } from 'msw';
import config from '../../config';

const baseURL = config.apiUrl;

describe('Modal component', () =&amp;gt; {
  it('should render modal when prompted', async () =&amp;gt; {
    server.use(
      rest.get(`${baseURL}/post/get-posts`, (req, res, ctx) =&amp;gt; {
        return res(ctx.json([]));
      })
    );
    const store = setupStore();
    store.dispatch(setIsAuthenticated(true));
    store.dispatch(setUser(mockUser));
    const { baseElement } = renderWithProviders(&amp;lt;App /&amp;gt;, {
      store,
      initialRoutes: ['/dashboard']
    });

    await waitForElementToBeRemoved(() =&amp;gt; screen.queryByTestId('main loader'));
    expect(
      await screen.findByText('Create a new post to get started.')
    ).toBeInTheDocument();

    userEvent.click(screen.getByTestId('create-post-btn'));

    const modal = getQueriesForElement(baseElement).queryByTestId('modal');
    expect(modal).toBeInTheDocument();
  });
});

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

&lt;/div&gt;



&lt;p&gt;After this change, I could now see the modal correctly rendered in the test:&lt;/p&gt;

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

&lt;p&gt;Mr.portal, I wanted to see you for ages!&lt;/p&gt;

&lt;h2&gt;
  
  
  Why baseElement instead of screen
&lt;/h2&gt;

&lt;p&gt;But why do we need to use &lt;code&gt;baseElement&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;When you use &lt;code&gt;screen&lt;/code&gt;, it only looks inside the subcomponents of a component passed to render function. In my example, it looks into &lt;code&gt;&amp;lt;App/&amp;gt;&lt;/code&gt; and its children but not what sits outside &lt;code&gt;&amp;lt;App/&amp;gt;&lt;/code&gt; component.&lt;/p&gt;

&lt;p&gt;On the other hand, &lt;code&gt;baseElement&lt;/code&gt; points to the entire rendered components, which include &lt;code&gt;&amp;lt;App/&amp;gt;&lt;/code&gt; and modals that are rendered in a &lt;code&gt;portal&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That is why the test failed with &lt;code&gt;screen&lt;/code&gt; but succeeded with &lt;code&gt;baseElement&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Caution
&lt;/h2&gt;

&lt;p&gt;There are other ways to render a modal in React applications and each method requires different approaches to test the modal component.&lt;/p&gt;

&lt;p&gt;So it is always important to be aware of where and how the modal you are testing is rendered and tailor your test accordingly!&lt;/p&gt;

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

&lt;p&gt;Knowing how to test components rendered in portals is definitely useful given that many applications have designs where important user interactions take place within these components. &lt;/p&gt;

&lt;p&gt;Thanks for reading this article.&lt;/p&gt;

&lt;p&gt;See ya!&lt;/p&gt;

</description>
      <category>react</category>
      <category>testing</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Persisting state on page refresh in React/Redux app</title>
      <dc:creator>mihomihouk</dc:creator>
      <pubDate>Mon, 10 Jul 2023 10:09:36 +0000</pubDate>
      <link>https://dev.to/mihomihouk/persisting-state-on-page-refresh-in-reactredux-app-58cf</link>
      <guid>https://dev.to/mihomihouk/persisting-state-on-page-refresh-in-reactredux-app-58cf</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Refreshing a page is a common behaviour for app users. However, when using Redux, the state stored in the Redux store is reset on page refresh. This can be problematic for apps with protected routes, as users may be redirected to the login page even if they were previously authenticated.&lt;/p&gt;

&lt;p&gt;In this article, we will explore several solutions, including session/local storage and Redux Persist, as well as their limitations to create a more user-friendly experience!&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem
&lt;/h2&gt;

&lt;p&gt;Consider the following example of a protected route:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { useLocation, Navigate } from 'react-router-dom';
import { useAppSelector } from '../hooks/hooks';
import React from 'react';
import { LOG_IN_PATH } from './routes';

interface SecureRouteProps {
  children?: React.ReactNode;
}
export const SecureRoute: React.FC&amp;lt;SecureRouteProps&amp;gt; = ({ children }) =&amp;gt; {
  const location = useLocation();
  const { isAuthenticated } = useAppSelector((state) =&amp;gt; state.auth);

  if (!isAuthenticated) {
    return &amp;lt;Navigate to={LOG_IN_PATH} state={{ from: location }} replace /&amp;gt;;
  }

  return &amp;lt;&amp;gt;{children}&amp;lt;/&amp;gt;;
};

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

&lt;/div&gt;



&lt;p&gt;The problem occurs when the user refreshes the page, causing the &lt;code&gt;isAuthenticated&lt;/code&gt; state to revert to its initial value, leading to an unnecessary redirect to the login page.&lt;/p&gt;

&lt;p&gt;This can be very frustrating for users as they have already been signed in. How can we avoid this unnecessary redirection while ensuring our pages are protected from unauthenticated users?&lt;/p&gt;

&lt;h2&gt;
  
  
  Common solutions
&lt;/h2&gt;

&lt;p&gt;There are several solutions to prevent this unnecessary redirection on page refresh. One approach involves using session/local storage in combination with Redux.&lt;/p&gt;

&lt;p&gt;In this approach, we need to write some additional code to add, read and remove a certain item from &lt;code&gt;sessionStorage&lt;/code&gt; or &lt;code&gt;localStorage&lt;/code&gt; alongside updating Redux.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use session storage + Redux
&lt;/h3&gt;

&lt;p&gt;If I implement this in my project, the change would look like this:&lt;/p&gt;

&lt;p&gt;Store the authentication status in session storage after successful login:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sessionStorage.setItem('isAuthenticated', 'true');
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the protected route component, check the authentication status by getting the state stored in session storage. Then, if the user is not authenticated, redirect them to the login page:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const SecureRoute: React.FC&amp;lt;SecureRouteProps&amp;gt; = ({ children }) =&amp;gt; {
  const { pathname } = useLocation();
  const isAuthenticated = sessionStorage.getItem('isAuthenticated') === 'true';

  if (!isAuthenticated) {
    const redirect = encodeURIComponent(pathname);
    return (
      &amp;lt;Navigate
        to={{
          pathname: '/login',
          state: { from: redirect },
        }}
      /&amp;gt;
    );
  }

  return &amp;lt;&amp;gt;{children}&amp;lt;/&amp;gt;;
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And finally, clear the stored authentication status on logout.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sessionStorage.removeItem('isAuthenticated');
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Use local storage + Redux
&lt;/h3&gt;

&lt;p&gt;The second approach is to use local storage and this is very similar to the first approach.&lt;/p&gt;

&lt;p&gt;The code is almost identical. We just need to replace the &lt;code&gt;sessionStorage&lt;/code&gt; in the example above with &lt;code&gt;localStorage&lt;/code&gt; (e.g., &lt;code&gt;const isAuthenticated = localStorage.getItem('isAuthenticated') === 'true';&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;The major difference between using session storage and local storage is the duration of the state persistence. While the state stored in session storage gets reset once the tab closes, local storage allows the state to persist even if the user closes and reopens the browser.&lt;/p&gt;

&lt;p&gt;These are already great solutions. However, one drawback of using session/local storage directly with Redux is that it requires adding additional code to every place where the state needs to be persisted. This can increase the complexity of the code and make it more susceptible to unexpected errors. &lt;/p&gt;

&lt;p&gt;So I took a simpler and optimised approach: using Redux Persist. &lt;/p&gt;

&lt;h2&gt;
  
  
  Redux Persist
&lt;/h2&gt;

&lt;p&gt;Redux Persist is a library designed to allow applications to preserve their state even on page refresh. &lt;/p&gt;

&lt;p&gt;It is really easy to incorporate Redux Persist in our existing React/Redux app. The only thing we need to do is to make a small modification to the store and insert a wrapper provided by Redux Persist in a higher component. Doing so eliminates the need to add additional state updates in multiple pages/components.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to integrate Redux Persist
&lt;/h2&gt;

&lt;p&gt;Let's look at how I updated my code to use Redux Persist.&lt;/p&gt;

&lt;p&gt;Install the Redux Persist library:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install redux-persist
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Update store config
&lt;/h3&gt;

&lt;p&gt;Next, following &lt;a href="https://github.com/rt2zz/redux-persist" rel="noopener noreferrer"&gt;their official document&lt;/a&gt;, I added some changes to my store. &lt;/p&gt;

&lt;p&gt;Before:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { PreloadedState, configureStore } from '@reduxjs/toolkit';
import modalReducer from '../slices/modals-slice';
import postReducer from '../slices/posts-slice';
import authReducer from '../slices/auth-slice';
import userProfileReducer from '../slices/user-profile-slice';

export const store = configureStore({
  reducer: {
    modals: modalReducer,
    posts: postReducer,
    auth: authReducer,
    userProfile: userProfileReducer
  }
});
export const setupStore = (preloadedState?: PreloadedState&amp;lt;RootState&amp;gt;) =&amp;gt; {
  return store;
};

export const createPreloadedState = (
  customState: Partial&amp;lt;RootState&amp;gt;
): PreloadedState&amp;lt;RootState&amp;gt; =&amp;gt; {
  return {
    modals: { ...store.getState().modals, ...customState.modals },
    posts: { ...store.getState().posts, ...customState.posts },
    auth: { ...store.getState().auth, ...customState.auth },
    userProfile: { ...store.getState().userProfile, ...customState.userProfile }
  };
};
export type AppDispatch = typeof store.dispatch;
export type AppStore = ReturnType&amp;lt;typeof setupStore&amp;gt;;
export type RootState = ReturnType&amp;lt;typeof store.getState&amp;gt;;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import {
  PreloadedState,
  combineReducers,
  configureStore
} from '@reduxjs/toolkit';
import modalReducer from '../slices/modals-slice';
import postReducer from '../slices/posts-slice';
import authReducer from '../slices/auth-slice';
import userProfileReducer from '../slices/user-profile-slice';
import storage from 'redux-persist/lib/storage';
import { persistReducer, persistStore } from 'redux-persist';
import autoMergeLevel2 from 'redux-persist/es/stateReconciler/autoMergeLevel2';

const persistConfig = {
  key: 'root',
  storage,
  stateReconciler: autoMergeLevel2
};

const rootReducer = combineReducers({
  modals: modalReducer,
  posts: postReducer,
  auth: authReducer,
  userProfile: userProfileReducer
});

const persistedReducer = persistReducer&amp;lt;ReturnType&amp;lt;typeof rootReducer&amp;gt;&amp;gt;(
  persistConfig,
  rootReducer
);

export const store = configureStore({
  reducer: persistedReducer
});

export const persistor = persistStore(store);

export const setupStore = (preloadedState?: PreloadedState&amp;lt;RootState&amp;gt;) =&amp;gt; {
  return store;
};

export const createPreloadedState = (
  customState: Partial&amp;lt;RootState&amp;gt;
): PreloadedState&amp;lt;RootState&amp;gt; =&amp;gt; {
  return {
    modals: { ...store.getState().modals, ...customState.modals },
    posts: { ...store.getState().posts, ...customState.posts },
    auth: { ...store.getState().auth, ...customState.auth },
    userProfile: { ...store.getState().userProfile, ...customState.userProfile }
  };
};
export type AppDispatch = typeof store.dispatch;
export type AppStore = ReturnType&amp;lt;typeof setupStore&amp;gt;;
export type RootState = ReturnType&amp;lt;typeof store.getState&amp;gt;;

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

&lt;/div&gt;



&lt;p&gt;Let me briefly explain a few important points:&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const persistConfig = {
  key: 'root',
  storage,
  stateReconciler: autoMergeLevel2
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here &lt;code&gt;storage&lt;/code&gt; is shorthand for &lt;code&gt;storage: storage&lt;/code&gt;, meaning I chose localStorage to store the persisted state. You could choose sessionStorage and write &lt;code&gt;storage: storageSession&lt;/code&gt; instead.&lt;/p&gt;

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

&lt;p&gt;In the same object, we also see this somewhat unfamiliar property: &lt;code&gt;stateReconciler&lt;/code&gt;. This is where we choose how to merge persisted state with the initial Redux state. This is set to &lt;code&gt;autoMergeLevel1&lt;/code&gt; by default but we can pass &lt;code&gt;autoMergeLevel2&lt;/code&gt; or &lt;code&gt;hardSet&lt;/code&gt; as well. &lt;code&gt;hardset&lt;/code&gt; completely overrides the initial state in our reducer. On the other hand, both &lt;code&gt;autoMergeLevel1&lt;/code&gt; and &lt;code&gt;autoMergeLevel2&lt;/code&gt; allow the initial state to be merged with the persisted state with slightly different behaviour: &lt;code&gt;autoMergeLevel1&lt;/code&gt; only concerns top-level property values while &lt;code&gt;autoMergeLevel2&lt;/code&gt; merges two levels deep.&lt;/p&gt;

&lt;p&gt;To understand more about these differences, I highly recommend you to read &lt;a href="https://blog.logrocket.com/persist-state-redux-persist-redux-toolkit-react/#:~:text=This%20type%20of%20merging%20in,which%20merges%20two%20levels%20deep." rel="noopener noreferrer"&gt;this article&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Wrap root components with PersistGate
&lt;/h3&gt;

&lt;p&gt;In order to ensure that UI gets rendered only after the persisted state has been retrieved and saved to Redux, we also need to add &lt;code&gt;PersistGate&lt;/code&gt; to our root component.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
import { Provider } from 'react-redux';
import { persistor, store } from './store/store';
import App from './App';
import { disableReactDevTools } from '@fvilers/disable-react-devtools';
import { PersistGate } from 'redux-persist/integration/react';
import { MainLoader } from './components/main-loader';

if (process.env.NODE_ENV === 'production') disableReactDevTools();

const root = ReactDOM.createRoot(
  document.getElementById('root') as HTMLElement
);
root.render(
  &amp;lt;React.StrictMode&amp;gt;
    &amp;lt;Provider store={store}&amp;gt;
      &amp;lt;PersistGate loading={&amp;lt;MainLoader /&amp;gt;} persistor={persistor}&amp;gt;
        &amp;lt;BrowserRouter&amp;gt;
          &amp;lt;App /&amp;gt;
        &amp;lt;/BrowserRouter&amp;gt;
      &amp;lt;/PersistGate&amp;gt;
    &amp;lt;/Provider&amp;gt;
  &amp;lt;/React.StrictMode&amp;gt;
);

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

&lt;/div&gt;



&lt;p&gt;We could pass a customised loading component to the loading prop inside &lt;code&gt;PersistGate&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is it!&lt;br&gt;
After these few changes, I could see refreshing a page no longer redirect users to the login page. The state still persists after closing the browser too, allowing us to access the app again without going through the login process.&lt;/p&gt;

&lt;h3&gt;
  
  
  Limitation
&lt;/h3&gt;

&lt;p&gt;It's important to note that persisting sensitive data, such as access tokens or user IDs, in local or session storage can introduce security vulnerabilities, particularly cross-site scripting (XSS) attacks. These approaches might offer improved user experience by maintaining user state across page refreshes, but they can also expose sensitive information to malicious actors.&lt;/p&gt;

&lt;p&gt;When sensitive data is stored in client-side storage, it becomes accessible to JavaScript code running in the user's browser. If an attacker manages to inject malicious code into your application through XSS, they can retrieve and misuse the stored data, compromising user security.&lt;/p&gt;

&lt;p&gt;I will explore more secure approaches to this in my future article.&lt;/p&gt;

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

&lt;p&gt;Redirecting users to the login page on every reload can lead to a poor user experience. This could be overcome by using session/local storage.  Redux Persist simplifies this process by automatically persisting and rehydrating your Redux state.&lt;/p&gt;

&lt;p&gt;However, it is very important to be aware of the security implications of storing data in local/session storage to protect users from malicious attacks. &lt;/p&gt;

</description>
      <category>react</category>
      <category>redux</category>
      <category>programming</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Deploy your full stack React &amp; Node.js app</title>
      <dc:creator>mihomihouk</dc:creator>
      <pubDate>Mon, 03 Jul 2023 18:04:41 +0000</pubDate>
      <link>https://dev.to/mihomihouk/deploy-your-full-stack-react-nodejs-app-1192</link>
      <guid>https://dev.to/mihomihouk/deploy-your-full-stack-react-nodejs-app-1192</guid>
      <description>&lt;p&gt;Are you ready to take your full-stack application from development to production but unsure of the necessary steps and potential pitfalls along the way? Deploying a full-stack application requires careful planning and consideration to ensure a smooth transition and a successful launch. Last month, I deployed my application and it took me a week until I finally saw it running perfectly in production. &lt;/p&gt;

&lt;p&gt;In this article, I will share with you the small and big mistakes I made and points to bear in mind in order to successfully deploy your full-stack application. From understanding the build command and customising it for your project to selecting a hosting service and setting up a production database, we will cover key aspects that can make or break your deployment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understand build
&lt;/h2&gt;

&lt;p&gt;Let's understand what the &lt;code&gt;build&lt;/code&gt; command means before deploying your application. You can skip this part if you are already comfortable with the concept and what it actually means to our deployment.&lt;/p&gt;

&lt;p&gt;Basically, we need to run &lt;code&gt;build&lt;/code&gt; command to make our files deployable. This command creates a build folder in the root of our application and stores optimised files, including static resources, css, html, JavaScript files, used in your application inside this folder. The content of the folder gets updated if changes are added to these files.&lt;/p&gt;

&lt;h2&gt;
  
  
  Customise build command for your project
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;build&lt;/code&gt; command, like other scripts, can be customised in &lt;code&gt;package.json&lt;/code&gt;. What should be included here totally depends on your project. For example, you might need to include logic to convert TypeScript code to JavaScript code if you are using TypeScript.&lt;/p&gt;

&lt;p&gt;While it may seem arbitrary, it is very important to include all the steps required for your particular project in this command; otherwise, you might encounter some errors in deployment. So let's take a little moment, search for what you should include, and customise your &lt;code&gt;build&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;The frontend build script in my recent project looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"build": "tsc &amp;amp;&amp;amp; react-scripts build"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And this is its backend script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"build": "tsc &amp;amp;&amp;amp; prisma generate &amp;amp;&amp;amp; prisma migrate deploy"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let me explain these scripts.&lt;/p&gt;

&lt;h3&gt;
  
  
  Compile TypeScript into JavaScript
&lt;/h3&gt;

&lt;p&gt;If you are using TypeScript in your project, please do not forget to include &lt;code&gt;tsc&lt;/code&gt; in your build command. This command converts all TypeScript codes into JavaScript codes, making your files executable in production.&lt;/p&gt;

&lt;h3&gt;
  
  
  Set up access to the database in production
&lt;/h3&gt;

&lt;p&gt;In my project, I am using a combination of Prisma and PostgreSQL to manage its database. The second command, &lt;code&gt;prisma generate&lt;/code&gt;, generates Prisma Client, which allows me to interact with my database to implement CRUD operations (e.g., creating a new item in a table). The third command, &lt;code&gt;prisma migrate deploy&lt;/code&gt;, on the other hand, applies new structural modifications (e.g., adding a new table) to my database.&lt;/p&gt;

&lt;p&gt;As you can see, what should be included in your build scripts really depends on which technologies you are using and how you manage your codebase and database. &lt;/p&gt;

&lt;h2&gt;
  
  
  Deploy with a hosting service
&lt;/h2&gt;

&lt;p&gt;I used &lt;a href="https://render.com/" rel="noopener noreferrer"&gt;Render&lt;/a&gt; to deploy my application. Render is one of the most popular web hosting services today. It is really easy to set up,  and we can use the basic service for free. &lt;/p&gt;

&lt;p&gt;To give you an overview, I set up my hosted client as "static site", my hosted server as "web service", and created a PostgreSQL database for production on Render. &lt;/p&gt;

&lt;p&gt;My dashboard looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fezgzu5jrjg3exvk7nyit.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fezgzu5jrjg3exvk7nyit.png" alt="render dashboard" width="800" height="151"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Set up the client
&lt;/h3&gt;

&lt;p&gt;First, I set up my client hosting following &lt;a href="https://render.com/docs/deploy-create-react-app" rel="noopener noreferrer"&gt;their documentation&lt;/a&gt; for a Create React App. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fju2dvmpx815wqwcouxdr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fju2dvmpx815wqwcouxdr.png" alt="render's instruction about static site" width="800" height="415"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the process, they ask me to add values to "Build Command" and "Publish Directory." Now that we know what the build command is, the instruction should be straightforward. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzj354zzn5s8vvn2gytu7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzj354zzn5s8vvn2gytu7.png" alt="static site commands" width="800" height="271"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I did not fully understand the build command when I initially set it up, and I excluded some critical implementations from the script. I hope you will not make the same mistake as me!&lt;/p&gt;

&lt;p&gt;Then, in the "Custom Domains" section, I set up a custom domain so I can use my unique domain purchased from &lt;a href="https://www.namecheap.com/" rel="noopener noreferrer"&gt;Namecheap&lt;/a&gt; instead of the default Render domain (e.g., &lt;code&gt;yourstaticsitename.onrender.com&lt;/code&gt;). You can find out how to set it up via this &lt;a href="https://render.com/docs/custom-domains#configuring-dns-to-point-to-render" rel="noopener noreferrer"&gt;link&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Once I saved the settings, I filled out all the environment variables used in my client.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fckcxhg3yqjyj2dzrvmdx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fckcxhg3yqjyj2dzrvmdx.png" alt="Environment variables" width="800" height="394"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Set up backend
&lt;/h3&gt;

&lt;p&gt;Next, I set up web service following &lt;a href="https://render.com/docs/deploy-node-express-app" rel="noopener noreferrer"&gt;their documentation for deploying a Node Express App&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;You might get confused when setting up the "Buid&amp;amp; Deploy" section. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp982nlcqbhr1or9pew4b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp982nlcqbhr1or9pew4b.png" alt="render's instruction about web service" width="800" height="412"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;First confusion might come from "Build Command." The example in the instruction uses just &lt;code&gt;yarn&lt;/code&gt; as its "Build Command." &lt;/p&gt;

&lt;p&gt;Yes, this &lt;code&gt;yarn&lt;/code&gt; command installs all the libraries required for the server and we definitely need to run this to deploy our server. However, as I explained at the beginning of this article, build command should include more logic than this if we are using TypeScript and/or need migration for our database. &lt;/p&gt;

&lt;p&gt;Thus, I added this value to "Build Command":&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1ki1nuxy9xq87bp4r9i0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1ki1nuxy9xq87bp4r9i0.png" alt="Build Command" width="800" height="179"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Secondly, instead of "Publish directory" that we saw when setting up a static site, we are asked to add "Start Command" value. &lt;/p&gt;

&lt;p&gt;This is because we need to START our server to handle requests from our client. And this command runs only after build command runs. &lt;/p&gt;

&lt;p&gt;So in my &lt;code&gt;package.json&lt;/code&gt;, I created my &lt;code&gt;start&lt;/code&gt; script like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; "start": "node ./build/src/index.js"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;node&lt;/code&gt; executes JavaScript code stored in the build folder. It is important to double-check the location of built main JS file in your project and write the right path to the file. The file could be named &lt;code&gt;app.js&lt;/code&gt; or &lt;code&gt;index.js&lt;/code&gt; depending on your project. &lt;/p&gt;

&lt;p&gt;Then I again set up a custom domain for the server and set environment variables as I did for the static site.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ensure the client and server are related subdomains
&lt;/h2&gt;

&lt;p&gt;If you do not use cookies, please skip this section. &lt;br&gt;
But if you do, this is really important.&lt;/p&gt;

&lt;p&gt;I am using cookies to authenticate users in my application. In short, when a user logs in, my server issues an access token and passes it to the client via cookies. Then, the client's token is checked on the server every time the client makes a request.&lt;/p&gt;

&lt;p&gt;This exchange via cookies is only possible when they are sharing the same root domain. For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;www.rootdomain.com&lt;/code&gt; and &lt;code&gt;api.rootdomain.com&lt;/code&gt; can exchange cookies.&lt;/li&gt;
&lt;li&gt;But &lt;code&gt;www.rootdomain.com&lt;/code&gt; and &lt;code&gt;api.differentdomain.com&lt;/code&gt; cannot exchange cookies.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you run your application in production and see no cookies in Developer tools, it might be because of this domain restriction. &lt;/p&gt;

&lt;p&gt;For those who want to see more examples like this, please check out this &lt;a href="https://www.youtube.com/watch?v=xgW0XjOeusY" rel="noopener noreferrer"&gt;video&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Create a production database
&lt;/h2&gt;

&lt;p&gt;After all of these setups, I was still failing to connect my server with the client. &lt;/p&gt;

&lt;p&gt;And I finally learned that I need to make my database accessible by my server in production!&lt;/p&gt;

&lt;p&gt;PostgreSQL database has a "connection URL", a URL that the application server uses to connect to the database. The connection URL looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;user@localhost:5433/mydb?options=-c%20synchronous_commit%3Doff
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As explained in &lt;a href="https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING" rel="noopener noreferrer"&gt;this documentation&lt;/a&gt;, &lt;code&gt;localhost:5433&lt;/code&gt; points to a port. And this was the port set up by PostgreSQL by default and I continued using this default port in production.&lt;/p&gt;

&lt;p&gt;In fact, this was the very cause of my problem.&lt;br&gt;
This "local" database is only accessible from the application running on the same machine, meaning it is impossible for the deployed server to make a connection with it.&lt;/p&gt;

&lt;p&gt;So what did I do?&lt;/p&gt;

&lt;p&gt;There were two options to overcome this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Build a production database and use it both from the development and production environment.&lt;/li&gt;
&lt;li&gt;Build a production database and use it only in production.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In web development, the second option is regarded as better since having a separate database can protect valuable information from accidental modifications and &lt;br&gt;
unauthorised access.&lt;/p&gt;

&lt;p&gt;Fortunately, Render has a great service with which we can create a &lt;a href="https://render.com/docs/databases" rel="noopener noreferrer"&gt;hosted production PostgreSQL database&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Its set-up is not as complicated as it seems but there are several things to bear in mind:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The web service and database should be in the same region.&lt;/li&gt;
&lt;li&gt;Dynamically pass the database URL to your server so that the server can connect the local database in development and the hosted database in production.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It might be a good idea to look at the database hosting services when choosing a provider to deploy your application.&lt;/p&gt;

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

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

&lt;p&gt;As we have seen, deploying a full-stack application involves several crucial steps that require good knowledge of the technologies used in the application and our decision-making. By customising the build command, setting up hosting services, and ensuring proper database configuration, you can successfully deploy your application in a production environment.  &lt;/p&gt;

&lt;p&gt;Thank you so much for reading this article if you made it this far! Whether you are about to start deploying your application or experiencing problems in deployment, I hope it could offer you some ideas.&lt;/p&gt;

&lt;p&gt;Happy deploying!&lt;/p&gt;

</description>
      <category>database</category>
      <category>react</category>
      <category>node</category>
      <category>postgres</category>
    </item>
    <item>
      <title>“Timed out” error in React/Redux app test: This is how I found my solution</title>
      <dc:creator>mihomihouk</dc:creator>
      <pubDate>Fri, 23 Jun 2023 15:47:09 +0000</pubDate>
      <link>https://dev.to/mihomihouk/timed-out-error-in-reactredux-app-test-this-is-how-i-found-my-solution-1j9j</link>
      <guid>https://dev.to/mihomihouk/timed-out-error-in-reactredux-app-test-this-is-how-i-found-my-solution-1j9j</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Testing is an integral part of software development, ensuring that our applications function as intended and deliver a reliable user experience. But testing React applications, especially when dealing with asynchronous operations and state changes, can be a bit tricky. In this article, we'll dive into a challenge I recently encountered during React testing: handling "Timed out" errors when waiting for asynchronous elements to appear.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I wanted to test
&lt;/h2&gt;

&lt;p&gt;I'm developing a React app (created with create-react-app) with Redux. I was trying to test one of the components using react-testing-library and Jest to ensure it renders the two elements below on visiting the dashboard: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;First, loading spinner &lt;/li&gt;
&lt;li&gt;Second, fetched list of posts&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  My Initial test code
&lt;/h2&gt;

&lt;p&gt;Here is the test I wrote.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;describe('PostList', () =&amp;gt; {
  it('should render a post list data after loading', async () =&amp;gt; {
    renderWithProviders(&amp;lt;PostList /&amp;gt;);
    const loadingEl = await waitFor(() =&amp;gt; screen.findByTestId('main loader'));
    expect(loadingEl).toBeInTheDocument();
    const postEl = await waitFor(() =&amp;gt; screen.findByText('test'));
    expect(postEl).toBeInTheDocument();
  });
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The overview of the testing steps is:&lt;/p&gt;

&lt;p&gt;1.Render the component&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; renderWithProviders(&amp;lt;PostList /&amp;gt;);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This &lt;code&gt;renderWithProviders&lt;/code&gt; is a reusable custom render function that wraps components with Redux providers. Please visit &lt;a href="https://redux.js.org/usage/writing-tests" rel="noopener noreferrer"&gt;this link &lt;/a&gt;if you want to learn more about it.&lt;/p&gt;

&lt;p&gt;2.Ensure loading spinner appears&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const loadingEl = await waitFor(() =&amp;gt; screen.findByTestId('main loader'));
expect(loadingEl).toBeInTheDocument();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I set a testId for the loading spinner, which appears immediately after asynchronous operation starts like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const override: CSSProperties = {
  display: "block",
  margin: "0 auto",
  borderColor: "red"
};

export const MainLoader = () =&amp;gt; {
  return (
    &amp;lt;div className="w-full h-full flex items-center" data-testid="main loader"&amp;gt;
      &amp;lt;PropagateLoader
        size={30}
        cssOverride={override}
        aria-label="Loading Spinner"
      /&amp;gt;
    &amp;lt;/div&amp;gt;
  );
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3.Ensure post&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const postEl = await waitFor(() =&amp;gt; screen.findByText('test'));
 expect(postEl).toBeInTheDocument()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I use &lt;a href="https://mswjs.io/" rel="noopener noreferrer"&gt;Mock service worker (msw)&lt;/a&gt; to intercept API call. This “test” is one value of mocked data. The mocked data returned instead of actual data via a handler when GET request is called.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const mockPostItem1: Post = {
  id: "123",
  author: mockUser,
  caption: "test",
  createdAt: "2023-06-19T15:30:00Z",
  files: [
    {
      id: "123",
      fileName: "Vegetable garden",
      size: "60b170c0-7673-46c4-a71d-861845ae9097",
      mimetype: "image/jpeg",
      alt: "Vegetable garden",
      portraitFileKey:
        "b269db9743e39238bb1babce7e70fb0db134f45dc222efac56e324016b764xxx",
      squareFileKey:
        "adked8886bd2f14f824914e5e87387136471a4aaf630f1073bcdg7cba",
      portraitFileUrl: "/image/mock-post-image.png?"
    }
  ],
  totalLikes: 5,
  comments: [],
  totalComments: 7,
  updatedAt: "2023-06-19T15:30:00Z"
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { rest } from "msw";
import config from "../config";
import { mockPostItem1 } from "./posts";
import { mockUser } from "./user";

const baseURL = config.apiUrl;

export const handlers = [
  rest.get(`${baseURL}/auth/${mockUser.id}`, (req, res, ctx) =&amp;gt; {
    return res(
      ctx.status(200),
      ctx.json({ data: { user: mockUser, posts: mockPostItem1 } })
    );
  })
];
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So if the fetching is successful, the text “test” should appear in the DOM.&lt;/p&gt;

&lt;h2&gt;
  
  
  Error Message
&lt;/h2&gt;

&lt;p&gt;When I ran the test, the elements (spinner and fetched data) never appeared. My console showed this error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;● PostList › should render a post list data after loading

    Timed out in waitFor.

      157 |   it('should render a post list data after loading', async () =&amp;gt; {
      158 |     renderWithProviders(&amp;lt;PostList /&amp;gt;);
    &amp;gt; 159 |     const loadingEl = await waitFor(() =&amp;gt; screen.findByTestId('main loader'));
          |                                    ^
      160 |     expect(loadingEl).toBeInTheDocument();
      161 |     const postEl = await waitFor(() =&amp;gt; screen.findByText('test'));
      162 |     expect(postEl).toBeInTheDocument();

      at waitForWrapper (node_modules/@testing-library/dom/dist/wait-for.js:166:27)
      at Object.&amp;lt;anonymous&amp;gt; (src/container/post-list.test.tsx:159:36)

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  What does it mean!?
&lt;/h3&gt;

&lt;p&gt;The "Timed out in waitFor" error message typically occurs when using the &lt;strong&gt;&lt;code&gt;waitFor&lt;/code&gt;&lt;/strong&gt; function. It indicates that the specified condition being waited for did not occur within the designated timeout period (in this case default timeout of 1,000 milliseconds).&lt;/p&gt;

&lt;p&gt;And as you can see below, the DOM was showing the default UI that I prepared for the case when there’s no post list instead of a mocked post list:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div
  class="flex justify-center items-center h-screen"
&amp;gt;
  &amp;lt;div
    class="text-center"
  &amp;gt;
    &amp;lt;h3
      class="text-3xl font-bold mb-4"
    &amp;gt;
      No posts yet
    &amp;lt;/h3&amp;gt;
    &amp;lt;p
      class="text-lg mb-6"
    &amp;gt;
      Create a new post to get started.
    &amp;lt;/p&amp;gt;
    &amp;lt;button
      class="w-full bg-primary-500 text-white inline-block p-2 rounded-lg hover:bg-opacity-75"
      tabindex="0"
      type="button"
    &amp;gt;
      Create Post
    &amp;lt;/button&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So what basically the error tells us is that the expected data is not appearing within 1 second.&lt;/p&gt;

&lt;h2&gt;
  
  
  Narrowing scope
&lt;/h2&gt;

&lt;p&gt;So I could know somehow the expected data is not rendered in the DOM within the timeout. &lt;/p&gt;

&lt;p&gt;Yes, this is a very vague error...&lt;/p&gt;

&lt;p&gt;So to start, I asked myself several questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is state change handled properly in this specific test?&lt;/li&gt;
&lt;li&gt;Is simply asynchronous transaction taking too much time?&lt;/li&gt;
&lt;li&gt;Is api URL in my handler set correctly?&lt;/li&gt;
&lt;li&gt;Are the versions of packages used in the test compatible to each other?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In answering the questions above, I then tried to:&lt;/p&gt;

&lt;h3&gt;
  
  
  1.Add timeout to asynchronous operations
&lt;/h3&gt;

&lt;p&gt;One possibility of the cause of the error was the asynchronous operation was simply taking long time. If that’s the case, the error could be solved by extending timeout like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;describe('PostList', () =&amp;gt; {
  it('should render a post list data after loading', async () =&amp;gt; {
    renderWithProviders(&amp;lt;PostList /&amp;gt;);
    const loadingEl = await waitFor(() =&amp;gt; screen.findByTestId('main loader'), {timeout: 3000});  // Set a specific time here
    expect(loadingEl).toBeInTheDocument();
    const postEl = await waitFor(() =&amp;gt; screen.findByText('test'));
    expect(postEl).toBeInTheDocument();
  });
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In theory, by adding timeout, the test now waits a bit longer until the loading spinner appears.&lt;/p&gt;

&lt;h3&gt;
  
  
  2.Store env variable used in API call locally
&lt;/h3&gt;

&lt;p&gt;Another possibility I thought of was API URL. In my app, I'm storing all env variables including API URL in Doppler and I was running test codes without using any env file. I thought somehow the env variables are not accessible when running tests, and thus test might have failed.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.Change package versions
&lt;/h3&gt;

&lt;p&gt;I also changed the versions of &lt;code&gt;axios&lt;/code&gt; and &lt;code&gt;msw&lt;/code&gt; as I’d seen &lt;a href="https://github.com/mswjs/msw/issues/1125" rel="noopener noreferrer"&gt;some reports&lt;/a&gt; on interception errors due to the compatibility between the two packages.  &lt;/p&gt;

&lt;h3&gt;
  
  
  4.Investigate &lt;code&gt;renderWithProvider&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Moreover, I revisited my code that handles state change in testing mode making sure there is no typo or misconfiguration.&lt;/p&gt;

&lt;h3&gt;
  
  
  5.Check other API call
&lt;/h3&gt;

&lt;p&gt;Just to double check this is not the problem of state handling, I wrote another test for different components that also uses API call. It actually threw the same error.&lt;/p&gt;

&lt;p&gt;After all these attempts, I still could not see any progress in my test! &lt;/p&gt;

&lt;p&gt;The problem could be my set up of Redux, mws, or my testing code…&lt;/p&gt;

&lt;p&gt;Yes…&lt;br&gt;
I completely ran out of ideas!&lt;/p&gt;
&lt;h2&gt;
  
  
  Reaching out to communities
&lt;/h2&gt;

&lt;p&gt;I’ve tried my best and still had no luck. &lt;br&gt;
So this is the time to ask others for help!&lt;/p&gt;
&lt;h3&gt;
  
  
  1.Stack Overflow
&lt;/h3&gt;

&lt;p&gt;I’ve first reached out to stack overflow. I prepared a document where I explained what I want to achieve, the error I’ve seen and what I tried to solve it. &lt;/p&gt;

&lt;p&gt;Unfortunately I didn’t get any response from other members.&lt;/p&gt;

&lt;p&gt;But this process actually helped me come up with other possibilities and organise my understanding about the problem.&lt;/p&gt;
&lt;h3&gt;
  
  
  2.Testing channel in Reactiflux Discord community
&lt;/h3&gt;

&lt;p&gt;Out of desperation, I asked for help in a Discord community dedicated to React. I’d never posted anything in this community before but I’ve seen people helping each other very actively!&lt;/p&gt;

&lt;p&gt;Surprisingly, a very kind person responded my post in a matter of one hour after I posted my issue, diving into my code, giving me really great tips!&lt;/p&gt;

&lt;p&gt;And yes! With his insights, I could finally find solutions, which I’d like to share in the next section.&lt;/p&gt;
&lt;h2&gt;
  
  
  Culprit
&lt;/h2&gt;

&lt;p&gt;So what was the problem? &lt;/p&gt;

&lt;p&gt;First, let’s take a look at my final working test code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;describe("PostList", () =&amp;gt; {

    it("should render a post list data after loading", async () =&amp;gt; {

    const store = setupStore();

    store.dispatch(setUser(mockUser));

    store.dispatch(setIsAuthenticated(true));

    renderWithProviders(&amp;lt;PostList /&amp;gt;, {store});

    await waitForElementToBeRemoved(() =&amp;gt; screen.queryByTestId("main loader"));

    const postEl = await screen.findByText("test");

    expect(postEl).toBeInTheDocument();

    });

});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you may notice, I added a number of changes to this test.&lt;/p&gt;

&lt;h3&gt;
  
  
  1.Redux state change
&lt;/h3&gt;

&lt;p&gt;One of the main issue was that the function that runs API call never ran. It runs only when there is a user but in the test, the user state was undefined.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;React.useEffect(() =&amp;gt; {
    let isMounted = true;

    const fetchData = async () =&amp;gt; {
      try {
        setIsLoading(true);
        if (user) {.     //If user is undefined, the code below never runs!!!
          const { data, alertMessage, isSuccess } = await getAllPosts(user.id);
          if (!isSuccess) {
            if (alertMessage === "Unauthorised") {
              navigate(LOG_IN_PATH);
            }
            alert(alertMessage);
            setIsLoading(false);
            return;
          }
          if (isMounted) {
            dispatch(updatePosts(data));
          }
        } else {
          navigate(LOG_IN_PATH);
        }
        setIsLoading(false);
      } catch (error) {
        console.log(error);
        alert("We failed to get information of posts.");
      }
    };
    fetchData();
    return () =&amp;gt; {
      isMounted = false;
    };
  }, []);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is because in my app:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;User logins and user information is immediately stored in Redux&lt;/li&gt;
&lt;li&gt;Using the user information, a list of posts including user’s posts is fetched &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So the solution was easy. I just needed to ensure user state gets updated with mockedUser BEFORE running the test (= a list of posts gets fetched).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const store = setupStore();

store.dispatch(setUser(mockUser)); // Update user

store.dispatch(setIsAuthenticated(true)); // Update auth state

renderWithProviders(&amp;lt;PostList /&amp;gt;, {store}); // Pass the state to redux provider
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2.First wait until loading indicator to be REMOVED
&lt;/h3&gt;

&lt;p&gt;In web applications, it’s a very common practice to render loading UI while fetching data. When testing asynchronous operations, we should ensure loading state is completely removed before searching for the elements we expected to be in the DOM.&lt;/p&gt;

&lt;h4&gt;
  
  
  Before:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const loadingEl = await waitFor(() =&amp;gt; screen.findByTestId('main loader'));
expect(loadingEl).toBeInTheDocument();
const postEl = await waitFor(() =&amp;gt; screen.findByText('test'));
expect(postEl).toBeInTheDocument();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In my previous code, I used &lt;code&gt;findByTestId&lt;/code&gt; to handle loading spinner. This might lead to an error as &lt;code&gt;findByTestId&lt;/code&gt; returns a promise that resolves when it finds matching id but there is no guarantee that an element with the id is removed from the DOM before the fetched post list appears. &lt;/p&gt;

&lt;h4&gt;
  
  
  After:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;await waitForElementToBeRemoved(() =&amp;gt; screen.queryByTestId("main loader"));

const postEl = await screen.findByText("test");

expect(postEl).toBeInTheDocument();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the other hand, &lt;code&gt;waitForElementToBeRemoved&lt;/code&gt; explicitly waits for an element to be removed from the DOM. So &lt;code&gt;waitForElementToBeRemoved&lt;/code&gt; is more appropriate to test a loading UI and the final fetched data.&lt;/p&gt;

&lt;h4&gt;
  
  
  waitFor or findBy?
&lt;/h4&gt;

&lt;p&gt;You may notice I also changed my code to use only &lt;code&gt;findByTestId&lt;/code&gt; and &lt;code&gt;findByText&lt;/code&gt; instead of using them with &lt;code&gt;waitFor&lt;/code&gt; to test asynchronous operations. &lt;/p&gt;

&lt;p&gt;While &lt;a href="https://testing-library.com/docs/dom-testing-library/api-async/" rel="noopener noreferrer"&gt;it seems&lt;/a&gt; not a bad practice to use &lt;code&gt;waitFor&lt;/code&gt; in this scenario, &lt;code&gt;findBy&lt;/code&gt; itself just does the job and it improves the code readability.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.Returned json object
&lt;/h3&gt;

&lt;p&gt;After finding the two solutions, I could see the loading spinner rendered in the DOM. &lt;/p&gt;

&lt;p&gt;It was progress. &lt;/p&gt;

&lt;p&gt;But I still failed to see the fetched post list and here is the final mistake that I found in my test code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rest.get(${baseURL}/post/get-posts, (req, res, ctx) =&amp;gt; {
    return res(ctx.json({data: [mockPostItem1, mockPostItem2]}));
  }),
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is mocked API call I set up using msw. &lt;/p&gt;

&lt;p&gt;Note that the object inside &lt;code&gt;ctx.json( )&lt;/code&gt;assumes fetched data belongs data property, which is slightly different from data structure dealt in my client code.&lt;/p&gt;

&lt;p&gt;So I corrected the code like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rest.get(${baseURL}/post/get-posts, (req, res, ctx) =&amp;gt; {
    return res(ctx.json([mockPostItem1, mockPostItem2]));
 })
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After implementing the necessary changes and addressing the issues I encountered, I was happy to see my test finally pass without any errors. &lt;/p&gt;

&lt;p&gt;I hope that by sharing my experience of trial and error and the solutions I found, this article can help others who might encounter similar challenges in testing react apps.&lt;/p&gt;

</description>
      <category>react</category>
      <category>testing</category>
      <category>redux</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Fight for Landing a Dev Job: What I Learned by Attending a React.js Meetup</title>
      <dc:creator>mihomihouk</dc:creator>
      <pubDate>Fri, 09 Jun 2023 12:26:21 +0000</pubDate>
      <link>https://dev.to/mihomihouk/fight-for-landing-a-dev-job-what-i-learned-by-attending-a-reactjs-meetup-5401</link>
      <guid>https://dev.to/mihomihouk/fight-for-landing-a-dev-job-what-i-learned-by-attending-a-reactjs-meetup-5401</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz1pp9g4rfebjz9p0a579.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz1pp9g4rfebjz9p0a579.JPG" alt="ReactJS Girls sticker and organiser's pamphlet" width="800" height="850"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction:
&lt;/h2&gt;

&lt;p&gt;In the competitive field of software development, securing a job as a developer can feel like a battle. I am a Junior full stack developer based in the UK with expertise in React, Typescript and Node. Since being made redundant at my previous company in March 2022, I’ve been on job hunt. In this article, I want to share my personal experience and the valuable insights I gained from attending a tech meetup, React.js Girls, as a job seeker.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is React.js Girls:
&lt;/h2&gt;

&lt;p&gt;React.js Girls is a meetup event that features female speakers sharing their knowledge and experiences with React. Although the speakers are women,  anyone can attend it as its audience regardless of gender.&lt;/p&gt;

&lt;p&gt;I was fortunate to secure a ticket to attend at the last minute. I learned about the event just a few days before the event day and found that all the seats were already taken. But I added myself to the waiting list in case of any cancellation. To my surprise, I received an email from the organiser on the afternoon of the event day, granting me a spot!&lt;/p&gt;

&lt;p&gt;The event took place in central London, starting from 6.45pm. They provide us pizza, snacks and drinks for free, which not only made it easier to strike up conversations with other attendees but also allowed me to focus on the talks without being distracted by hunger.&lt;/p&gt;

&lt;p&gt;We had three speakers covering topics such as React-testing-libraries/Jest, alternative React state management, and creating iInclusive workplace. While all the talks were inspiring and definitely worth digging into, I’d like to focus on what I, as a job seeker, learned from my first tech meetup experience. If you are interested in the talks, please find their recording on &lt;a href="https://www.youtube.com/@TechTalksYLD/featured" rel="noopener noreferrer"&gt;YouTube&lt;/a&gt; (As of 9 June, they haven’t uploaded the recording yet. But I’m sure they will!).&lt;/p&gt;

&lt;p&gt;What follows are the takeaways from the event!&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The power of meeting real people:
&lt;/h2&gt;

&lt;p&gt;It is a cliche but meetup is indeed good for networking. Well, in my case, it was good to learn how to engage with fellow developers. While I couldn’t make extensive connections due to time limitations, I had meaningful conversations about their career paths, opinions about remote working, technologies in use, and projects they are involved in. These conversations were not solely focused on professional topics; we also discussed cultural experiences, travel and future plans. I personally believe this is the true power of meetups - the ability to  build connections and relationships by sharing our views on broader topics as humans, spending time together in the same space, and engaging in face-to-face conversations. In this respect, meetups offer a different networking experience compared to social online platforms.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Reflecting on my practices and learn from senior developers:
&lt;/h2&gt;

&lt;p&gt;During the months of my job hunt, working on my personal portfolio project or learning new technologies left me feeling uncertain about my skills and knowledge. Attending the meetup allowed me to compare my own project experiences and learn from the practices and solutions shared by senior developers. Finding similarities in the challenges, solutions and the underlying logic between me and the speakers gave me assurance and confidence in my skills. I also learned new concepts, decision-making tips for testing, insights about a new state management framework and how it can be compared with Redux, and how to restructure our understanding of inclusivity to foster it in our team. The talks were based on real experiences, making the learnings highly adaptable and practical.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. What didn’t happen:
&lt;/h2&gt;

&lt;p&gt;Although I had some hope to get feedback on my project and advice for job hunting or career development, they did not happen in this event. Apparently, this meetup was not specifically focused on those aspects and I paid extra attention to what other people wanted to talk about rather than seeking help for myself. But you never know if someone in the meetup might be willing to offer assistance on the spot. So it might be worth preparing to show projects, CV and questions in case such opportunities arise!&lt;/p&gt;

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

&lt;p&gt;Attending a tech meetup could be intimidating or it may seem inconsequential if you’ve never experienced one before. But to me, it was a great experience in my quest for a dev job. Meeting like-minded people, gaining access to industry expertise, and assessing my current skills and knowledge had a positive impact on my motivation, connections and tech skills. &lt;/p&gt;

&lt;p&gt;Thanks for reading this article! If you have an open role at your organisation, please do let me know via &lt;a href="https://www.linkedin.com/in/miho-inagaki/" rel="noopener noreferrer"&gt;my linkedIn&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>job</category>
      <category>career</category>
      <category>react</category>
      <category>programming</category>
    </item>
    <item>
      <title>A Solution: How to successfully fetch data from Firestore Database</title>
      <dc:creator>mihomihouk</dc:creator>
      <pubDate>Sun, 27 Mar 2022 21:27:49 +0000</pubDate>
      <link>https://dev.to/mihomihouk/a-solution-how-to-successfully-fetch-data-from-firestore-database-421j</link>
      <guid>https://dev.to/mihomihouk/a-solution-how-to-successfully-fetch-data-from-firestore-database-421j</guid>
      <description>&lt;p&gt;When trying to fetch a set of data from Firestore Database, I stumbled into a pretty basic challenge: the data is not shown on the screen.&lt;/p&gt;

&lt;p&gt;This article suggests a solution for those who are learning Firebase and got caught by a similar problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  What was the Situation?
&lt;/h2&gt;

&lt;p&gt;When I was learning how to use Firebase V9 with a &lt;a href="https://www.udemy.com/course/build-web-apps-with-react-firebase/" rel="noopener noreferrer"&gt;Udemy course&lt;/a&gt;, I was struggling to get the data that I manually created on Cloud Firestore. &lt;/p&gt;

&lt;p&gt;I went through all the following processes twice just to make sure I don’t miss any essential steps to successfully fetch the data into my browser. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a Firebase Project&lt;/li&gt;
&lt;li&gt;Create a web app under the project&lt;/li&gt;
&lt;li&gt;Copy the config and paste on a file in my local project&lt;/li&gt;
&lt;li&gt;Initialise firebase in the file using the copied config&lt;/li&gt;
&lt;li&gt;Initialise firestore and export the particular database from the file&lt;/li&gt;
&lt;li&gt;Install the latest firebase &lt;/li&gt;
&lt;li&gt;Run the application at localhost:3000&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Problem
&lt;/h2&gt;

&lt;p&gt;No error message, all seems fine except that no data from the database was shown in the place that it should be.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Solution
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Did All The Process Again
&lt;/h3&gt;

&lt;p&gt;Scraping the project to the bin, I downloaded the course file again, wrote the code again by watching the course video very carefully, and made sure that I didn't miss any step.&lt;/p&gt;

&lt;h3&gt;
  
  
  Checked if My Code Looks Exactly the Same as the Tutorial
&lt;/h3&gt;

&lt;p&gt;No typo and no mistake. It’s impossible to be wrong.&lt;/p&gt;

&lt;h3&gt;
  
  
  Solution: Firebase Rules
&lt;/h3&gt;

&lt;p&gt;If my code is perfect, then the problem should be on the side of firebase. &lt;/p&gt;

&lt;p&gt;And here it is, the rules were written as below:&lt;/p&gt;

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

&lt;p&gt;The 'if false' part was the cause of the problem.&lt;/p&gt;

&lt;p&gt;If the part is false, no one can read and write the data.&lt;/p&gt;

&lt;p&gt;For the sake of experimenting with data-fetching for this tutorial, I temporarily edited the sentence to 'allow read, write: if true'.&lt;/p&gt;

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

&lt;p&gt;And now on the screen, I can see the data that I manually added on Firestore Database. This means the data is successfully fetched.&lt;/p&gt;

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

&lt;p&gt;Please note that this is a bad practice if you’re actually developing an app with personal information as it allows any person can access and change the contents.&lt;/p&gt;

&lt;p&gt;But if you’re just a beginner wanting to understand some basic functions of firebase and haven’t learned about the firebase rules yet, this could be a temporary solution.&lt;/p&gt;

&lt;p&gt;You can always come back and modify the rules as you go. &lt;/p&gt;

</description>
      <category>beginners</category>
      <category>firebase</category>
      <category>programming</category>
    </item>
    <item>
      <title>The reasons why beginners should join an experiential team development project before applying for a job.</title>
      <dc:creator>mihomihouk</dc:creator>
      <pubDate>Mon, 21 Mar 2022 23:55:19 +0000</pubDate>
      <link>https://dev.to/mihomihouk/the-reasons-why-beginners-should-join-an-experiential-team-development-project-before-applying-for-a-job-1hb8</link>
      <guid>https://dev.to/mihomihouk/the-reasons-why-beginners-should-join-an-experiential-team-development-project-before-applying-for-a-job-1hb8</guid>
      <description>&lt;p&gt;Have you ever thought that you have been practising coding alone for so long but there still seems to be a huge gap between your current self and a strong candidate for job hunting?&lt;/p&gt;

&lt;p&gt;If you are unsure about your preparedness and current skill, joining an experiential team development is definitely worth investing time.&lt;/p&gt;

&lt;p&gt;Here I will share my own experience of developing an application with other peer code learners and takeaway from being part of a team.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why did I decide to join?
&lt;/h2&gt;

&lt;p&gt;The event that led to me joining an experiential team development was quite simple: my mentor’s recommendation. Ever since I started to learn how to code, I had used a mentoring service through which I got monthly mentoring about my progress and technical advice from developers. But I have spent most of my time learning alone by using online tutorials and creating small apps to try out new skills. I was always feeling anxious about applying for a job without actually knowing how to develop an app in collaboration with other people.&lt;/p&gt;

&lt;p&gt;And one day, my mentor asked me to take part in a team development event that he hosted. At that time, I was nowhere near confident about my code. I had just finished 70% of my first big challenge, creating a Todo app with Next.js and React. But I kept asking myself, ‘When will the best timing come then?’ And my answer is ‘It will never come. I have basic skills so I should just jump into it!’ So I did it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What were my expectations for team development?
&lt;/h2&gt;

&lt;p&gt;There were two main expectations in participating in the team development.&lt;/p&gt;

&lt;p&gt;Understanding what issues would emerge in terms of communication within the team and my own skills, and how I should deal with them in team development.&lt;/p&gt;

&lt;p&gt;Getting a clearer idea on what are the gaps between a strong candidate for frontend engineer and what skills from the perspective of team development, and what kind of knowledge I should learn to close these gaps. &lt;/p&gt;

&lt;p&gt;As written below, my takeaways from experiential team development turned out to be far richer than my initial expectation.&lt;/p&gt;

&lt;h2&gt;
  
  
  What did we create?
&lt;/h2&gt;

&lt;p&gt;Here is the finished product that 8 of us all made together.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://todo-team-2-qigd0p0al-m8na.vercel.app/" rel="noopener noreferrer"&gt;https://todo-team-2-qigd0p0al-m8na.vercel.app/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this development, Next.js &amp;amp; React were used as the main framework and library while Recoil was introduced for state management. In addition, Chakra-UI was used as the framework for styling.&lt;/p&gt;

&lt;h2&gt;
  
  
  Languages/frameworks and other technologies used in the application
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Next.js&lt;/li&gt;
&lt;li&gt;React&lt;/li&gt;
&lt;li&gt;Recoil&lt;/li&gt;
&lt;li&gt;Chakra-UI&lt;/li&gt;
&lt;li&gt;Functions implemented in the application&lt;/li&gt;
&lt;li&gt;Add, edit, view details, delete&lt;/li&gt;
&lt;li&gt;Comment&lt;/li&gt;
&lt;li&gt;Markdown&lt;/li&gt;
&lt;li&gt;Pagination&lt;/li&gt;
&lt;li&gt;Scrolling&lt;/li&gt;
&lt;li&gt;Change Progress&lt;/li&gt;
&lt;li&gt;Change Priority&lt;/li&gt;
&lt;li&gt;Search&lt;/li&gt;
&lt;li&gt;Filters&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What did we try as a team?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Page transitions using Next.js routing&lt;/li&gt;
&lt;li&gt;Global state management using Recoil&lt;/li&gt;
&lt;li&gt;Layout creation using Chakra-UI&lt;/li&gt;
&lt;li&gt;Propose a feature or issue you would like to work on&lt;/li&gt;
&lt;li&gt;Resolving problems when errors occur&lt;/li&gt;
&lt;li&gt;Purpose/Background of Team Development&lt;/li&gt;
&lt;li&gt;Experience in team development, learning how to use Github, etc.&lt;/li&gt;
&lt;li&gt;Learning how to implement basic application functions by creating a todo list&lt;/li&gt;
&lt;li&gt;Learn how to write code that is easy to understand by others through code writing and code reviews based on the premise of team development&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What was my role in the team development?
&lt;/h2&gt;

&lt;p&gt;From early on in the development process, I was in charge of coordinating the schedule and agenda for the weekly meetings, as well as hosting the weekly online meetings. I also actively reviewed code as needed, helped other members dealing with errors, and managed the progress of the development.&lt;/p&gt;

&lt;h2&gt;
  
  
  What were some of the things we tried as a team?
&lt;/h2&gt;

&lt;p&gt;While we started off as strangers, we all learned to maintain smooth communication as time went by. Since app development involves a lot of individual work, it is necessary to make it easy for all members to know what each person is working on, and how much progress he or she is making. For this reason, we tried to create an atmosphere in which the entire team could easily ask for help from each other by clearly assigning responsibilities, and exchanging messages frequently.&lt;/p&gt;

&lt;h2&gt;
  
  
  What were some of the difficulties in team development?
&lt;/h2&gt;

&lt;p&gt;Especially for a while after the development started, I was scared of operating git. As we were developing as a team, I was worried too much about making mistakes, thinking 'What if this operation damages the entire application?' However, my mentor kept telling us 'Errors and failures are opportunities to learn. It would be rather strange if everything is perfect from the beginning.'Also, I soon learned that other team members felt the same way. Thanks to the advice and learning, my anxiety quickly disappeared.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are my impressions of participating in team development?
&lt;/h2&gt;

&lt;p&gt;First of all, from a technical standpoint, I was able to learn many frameworks and implementations that I had no experience with, and through code reviews, I was able to see the code that other members wrote, which greatly improved my knowledge and skills.&lt;/p&gt;

&lt;p&gt;The below are some of the things I found unique to team development, and therefore have given me a great deal of confidence in my job hunting.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Branch creation&lt;/li&gt;
&lt;li&gt;Creating pull requests on GitHub&lt;/li&gt;
&lt;li&gt;Code review&lt;/li&gt;
&lt;li&gt;Issue creation on GitHub&lt;/li&gt;
&lt;li&gt;Utilising the project function on GitHub&lt;/li&gt;
&lt;li&gt;Moving to a branch created by another member for code review&lt;/li&gt;
&lt;li&gt;Conflict resolution&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Next, regarding the software side, I learned the importance of progress management, MTG, and detailed (but not too detailed) communication among team members. I was also able to rediscover new possibilities and skills in my role as a leader during the course of the program.&lt;/p&gt;

&lt;p&gt;Finally, this team development also had an enormous impact on my own motivation. Over the period of self-learning, I had been feeling a bit stuck not knowing if the code I wrote was correct or not and whether I was on the right track in becoming a skilful frontend engineer. Engaging with other code learners through application development, in this respect, was of great encouragement as we could share the same difficulties and look for the solutions together.&lt;/p&gt;

&lt;h2&gt;
  
  
  Comments from other members (excerpts)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;I was able to improve my knowledge by learning that there are many different ways to achieve the same thing, and by learning things I didn't know.&lt;/li&gt;
&lt;li&gt;Having team members who get together every weekend to exchange opinions and communicate with each other has boosted my motivation, and I think it has been a very valuable experience.&lt;/li&gt;
&lt;li&gt;By being involved in team development, I was made aware once again of my weaknesses other than technical aspects.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  For those considering experiential team development
&lt;/h2&gt;

&lt;p&gt;If you are stuck in self-study, I definitely encourage you to participate in some events as well as the one-on-one meetings about code-learning and future career. Team development is one of them, and it will bring you valuable experience that you cannot get from self-study.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>career</category>
    </item>
  </channel>
</rss>
