<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Bionic Julia</title>
    <description>The latest articles on Forem by Bionic Julia (@bionicjulia).</description>
    <link>https://forem.com/bionicjulia</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%2F468267%2Fbfdef739-0ebb-4859-8589-a482637cbe8f.png</url>
      <title>Forem: Bionic Julia</title>
      <link>https://forem.com/bionicjulia</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/bionicjulia"/>
    <language>en</language>
    <item>
      <title>Creating a React Accordion Component Using Just CSS</title>
      <dc:creator>Bionic Julia</dc:creator>
      <pubDate>Sun, 08 May 2022 00:00:00 +0000</pubDate>
      <link>https://forem.com/bionicjulia/creating-a-react-accordion-component-using-just-css-h11</link>
      <guid>https://forem.com/bionicjulia/creating-a-react-accordion-component-using-just-css-h11</guid>
      <description>&lt;p&gt;About a year ago now, I wrote a blog post on &lt;a href="https://bionicjulia.com/blog/creating-accordion-component-react-typescript-tailwind"&gt;how to create an accordion component in React with Typescript and TailwindCSS&lt;/a&gt;. I recently needed to implement an accordion component again (this is why keeping a blog is so handy!), but this time in a codebase that doesn’t use TailwindCSS. Here’s a short follow-on to that post for how you’d create an accordion component if you were just using straight CSS. I'd recommend reading that post first if you're new to React, as it includes more detail on the order in which I built up the code.&lt;/p&gt;

&lt;p&gt;Note that I’ve left most of the styling of the overall component out and focused on the important ones that effect the transition.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;MutableRefObject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;appConfig&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../appConfig&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;AccordionProps&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ReactNode&lt;/span&gt;
  &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ReactNode&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Accordion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FC&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;AccordionProps&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;showExtraContent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setShowExtraContent&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setHeight&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0px&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;contentSpace&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;MutableRefObject&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HTMLDivElement&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;toggleAccordion&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;setShowExtraContent&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;previousState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;previousState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;setHeight&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;showExtraContent&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0px&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;contentSpace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scrollHeight&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;px`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"container"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;
        &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;toggleAccordion&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;img&lt;/span&gt;
          &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/assets/img/icons/chevron-up.svg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Chevron icon"&lt;/span&gt;
          &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;showExtraContent&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rotate&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; arrow`&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;
        &lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;contentSpace&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;maxHeight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"extra-content"&lt;/span&gt;
      &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here are the corresponding CSS styles.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.container&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;flex-direction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;column&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.arrow&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.3s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.rotate&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rotate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;180deg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.extra-content&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;overflow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;hidden&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;max-height&lt;/span&gt; &lt;span class="m"&gt;0.3s&lt;/span&gt; &lt;span class="n"&gt;ease-in-out&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Like this post? Read more from me at &lt;a href="https://bionicjulia.com/blog"&gt;https://bionicjulia.com/blog&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>react</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Using AMP Images in NextJS</title>
      <dc:creator>Bionic Julia</dc:creator>
      <pubDate>Sat, 30 Apr 2022 00:00:00 +0000</pubDate>
      <link>https://forem.com/bionicjulia/using-amp-images-in-nextjs-2k8</link>
      <guid>https://forem.com/bionicjulia/using-amp-images-in-nextjs-2k8</guid>
      <description>&lt;h3&gt;
  
  
  AMP - Accelerated Mobile Page
&lt;/h3&gt;

&lt;p&gt;First things first, what is &lt;strong&gt;AMP&lt;/strong&gt;? It stands for &lt;strong&gt;Accelerated Mobile Page&lt;/strong&gt; and is a web component framework that allows us to easily create user-first experiences for the web. It’s an open-source project by Google and Twitter, and was designed to create really fast mobile pages. If you want to read more about it, check out the &lt;a href="https://amp.dev/"&gt;official website&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  How NextJS integrates AMP
&lt;/h3&gt;

&lt;p&gt;NextJS makes it easy to integrate AMP components in your app, with an advanced feature that’s easily configurable. You can set the configuration on a page by page basis, deciding whether you want each particular page to be an AMP-only page (if the page has no NextJS or React client-side runtime) or a hybrid page (where the page will be rendered as traditional HTML by default, but where you can render AMP HTML by adding &lt;code&gt;?amp=1&lt;/code&gt; to the URL).&lt;/p&gt;

&lt;p&gt;If you’re coding up a hybrid page, NextJS provides a nice &lt;code&gt;useAmp&lt;/code&gt; hook to allow us to differentiate between the two modes. This allows us to create conditional JSX for instance, whereby if &lt;code&gt;useAmp&lt;/code&gt; returns &lt;code&gt;true&lt;/code&gt;, we render an AMP component, and a normal HTML element if otherwise. Read more about how to set this up in NextJS &lt;a href="https://nextjs.org/docs/api-reference/next/amp"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  amp-img component
&lt;/h3&gt;

&lt;p&gt;The AMP component I want to focus on today is the &lt;a href="https://amp.dev/documentation/components/amp-img/?format=websites"&gt;amp-img&lt;/a&gt; component. Images can really add to the feel and messaging of a web page...but at the cost of potentially slowing load times. The other potentially annoying thing is cumulative layout shift as images are loaded and rendered, which Google heavily penalises. AMP images can really help on both of these counts, as it smartly manages image resources during runtime, and additionally, places images in their rightful place to limit layout shift, due to the fact that we need to pass in an image’s explicit size.&lt;/p&gt;

&lt;p&gt;If you want to make a hybrid AMP image component in NextJS, it could look something like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Image&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next/image&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useAmp&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next/amp&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ImageWithAmp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isAmp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useAmp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;isAmp&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;amp&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="na"&gt;img&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;alt&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"responsive"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Image&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;alt&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the &lt;code&gt;amp-img&lt;/code&gt; component, &lt;code&gt;layout="responsive"&lt;/code&gt; means that the image will be resized based on the aspect ratio set by the weight and height, in response to the size of the viewport. The height and width figures set here don’t actually determine the size of the image that’s rendered - it’s solely used to determine the aspect ratio when &lt;code&gt;layout="responsive"&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Note that &lt;code&gt;amp-img&lt;/code&gt; is a block element (vs. a standard HTML &lt;code&gt;img&lt;/code&gt; element which is inline), so if you want to set a maximum height (or width) for your image, you’ll need to wrap your &lt;code&gt;amp-img&lt;/code&gt; in a container, and set the max-height &lt;strong&gt;on that container&lt;/strong&gt;. Alternatively, you can set &lt;code&gt;display: inline-block&lt;/code&gt; directly on your &lt;code&gt;amp-image&lt;/code&gt; instead. There are lots of other options, so be sure to read the &lt;a href="https://amp.dev/documentation/components/amp-img/?format=websites"&gt;official docs&lt;/a&gt; to see how you might best use this component for your needs.&lt;/p&gt;

&lt;p&gt;For the NextJS &lt;code&gt;Image&lt;/code&gt; component, the default layout is &lt;code&gt;intrinsic&lt;/code&gt; if no value is provided. This setting scales the dimensions of the image down for smaller viewports (where the viewport is smaller than one of the dimensions of your image), but maintains the original dimensions for larger viewports. The &lt;a href="https://nextjs.org/docs/api-reference/next/image"&gt;official docs&lt;/a&gt; has some really nice examples for the different options you have, so be sure to check them out.&lt;/p&gt;

&lt;p&gt;Thinking of utilising AMP components? Tell me about what components you’re using it on! I'm at &lt;a href="https://bionicjulia.com"&gt;https://bionicjulia.com&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>react</category>
      <category>nextjs</category>
      <category>mobile</category>
      <category>amp</category>
    </item>
    <item>
      <title>Moving From React to React Native</title>
      <dc:creator>Bionic Julia</dc:creator>
      <pubDate>Sat, 23 Apr 2022 00:00:00 +0000</pubDate>
      <link>https://forem.com/bionicjulia/moving-from-react-to-react-native-3bbf</link>
      <guid>https://forem.com/bionicjulia/moving-from-react-to-react-native-3bbf</guid>
      <description>&lt;p&gt;I’ve gone full circle and have recently gone back to developing for the web in my day job with React, after working with React Native for a year. Making the mindset shift from React to React Native, and now going back to React is not hard, but can be made significantly easier if an engineer goes into a move clearly knowing what the key similarities and differences are.&lt;/p&gt;

&lt;p&gt;With React Native still fresh in my mind, and as I’m easing back into React for web, I wanted to write this blog post, as this is what I wish I had read, before starting React Native for the first time a year ago. I wrote a &lt;a href="https://dev.to/blog/transitioning-from-react-to-react-native"&gt;blog post with my initial thoughts&lt;/a&gt;, way back when I first started with React Native, but this is a more fleshed out version.&lt;/p&gt;

&lt;h3&gt;
  
  
  But first...
&lt;/h3&gt;

&lt;p&gt;For those who have a limited idea about what React or React Native is, the main thing to understand is that React for web is an abstracted “framework” that renders down to HTML, which enables it to be read by a browser. React Native is specifically used to develop native apps (the common ones being iOS and Android) and thus renders down to native mobile elements (which differs for the various platforms). This means that React for web is designed to be run in a browser environment, versus React Native which is executed in a JS runtime, from within a native application.&lt;/p&gt;

&lt;p&gt;Whilst there’s enough common ground to enable developers to move from one to the other without too much friction, this does also mean that there are some very specific ways that React Native works, which will require a bit of a mindset shift when coming from web. I’ll start first by laying down the similarities, before covering the differences below.&lt;/p&gt;

&lt;h2&gt;
  
  
  Similarities
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Use of the React framework
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The most obvious one - whilst React Native is an abstracted framework for ultimately, mobile native platforms, it does still utilise React as a UI framework. This means that most of what you already understand around how React works, like component lifecycles, hooks and JSX transfers across.&lt;/li&gt;
&lt;li&gt;Following on from the point above, the fact that React Native uses React means that as a developer, you’ll be writing in JavaScript (or Typescript). You likely won’t need to delve into Swift or Kotlin, unless you’re having to deal with a specific native issue, or are having to develop features that React Native doesn’t yet bridge. (Saying that, it is helpful to be able to understand these languages, especially when it comes to bug fixing and troubleshooting!)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  JavaScript eco-system
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The other nice thing about developing with JS, is that you get to plug into the vibrant and massive JS eco-system. Granted, you won’t be able to utilise packages that rely on browser DOMs or browser APIs, but there are still a lot of other useful packages and tools you’ll already know. This includes libraries like React Query and Redux, and also dev tools like ESLint, Prettier and Jest.

&lt;ul&gt;
&lt;li&gt;If you’re interested, the reason this works is because React Native utilises a JS-bundler called Metro, which bundles up the application logic into a JS bundle. This is also what allows us to use fast refresh when developing at the JavaScript level.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;One thing to note with testing however, is that test runners and packages generally run with Node. Your React Native app will likely include, not just JS, but also native dependencies. You’ll therefore need to mock out your native dependencies when writing your tests with these JS-based testing libraries. This can be painful to say the least, and can make writing automated and end-to-end tests difficult. Testing React Native apps can therefore be a very manual process.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Differences
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Runtime environment
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The main one off the bat, that a new React Native developer will notice are the primitive elements you use in your JSX. There’re no such things as &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tags as these are HTML elements for the browser. What you’ll see instead are &lt;code&gt;&amp;lt;View&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;Text&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;Image&amp;gt;&lt;/code&gt; elements. Note that these are then further rendered down to native specific elements for iOS and Android.&lt;/li&gt;
&lt;li&gt;Following on from this, because we’re not running in a browser environment when using React Native, you won’t have access to browser APIs and the browser DOM. This means you won’t see event handlers like &lt;code&gt;onClick&lt;/code&gt; or &lt;code&gt;onMouseOver&lt;/code&gt;...which makes sense because the user will not be using a mouse when interacting with your mobile app, but instead (more likely than not), their finger. You’ll instead be dealing with &lt;code&gt;onPress&lt;/code&gt; or &lt;code&gt;onLongPress&lt;/code&gt; and other native gestures.&lt;/li&gt;
&lt;li&gt;While you might miss out on browser APIs, you’ll gain by having access to mobile specific features like accelerometer, screen view modes and haptic touch. ✌️&lt;/li&gt;
&lt;li&gt;The other big area to be aware of is how animations are programmed. With React for web, this can be done via CSS. With React Native, you’ll need to do this with the &lt;a href="https://reactnative.dev/docs/animated"&gt;Animated API&lt;/a&gt; or an external package like &lt;a href="https://github.com/software-mansion/react-native-reanimated"&gt;Reanimated&lt;/a&gt;. Note that performance is something you want to be cognisant about when using animations in React Native, as these are determined in the JavaScript thread, but needs to be communicated to the native UI thread which can gum things up.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Styling
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;This was probably the most annoying one to get to grips with as it requires a shift in thinking that I wasn’t really aware of. The main thing to know is that styling on React Native does not use CSS. It instead uses a styling API which utilises something akin to inline styling, where you pass styles (via objects) directly into the individual React Native elements.&lt;/li&gt;
&lt;li&gt;There is no cascading of styles in React Native! There’s also no such thing as selectors or nesting!&lt;/li&gt;
&lt;li&gt;This means that on the one hand, it feels incredibly manual and archaic when you first come across styling on RN. However, once it becomes second nature, it’s actually really nice in its simplicity as you never really have to deal with weird styles appearing from seemingly nowhere, higher up in the styling cascade.&lt;/li&gt;
&lt;li&gt;The one thing that is the same, is that both React for web and React Native uses the idea of a flexbox. The default &lt;code&gt;flex-direction&lt;/code&gt; in React Native is &lt;code&gt;column&lt;/code&gt;, rather than &lt;code&gt;row&lt;/code&gt; in CSS. The default &lt;code&gt;display&lt;/code&gt; setting in React Native is &lt;code&gt;flex&lt;/code&gt;, so you don’t need to explicitly declare this.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Navigation
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The main mental model shift is to think of “screens” in React Native, being ordered in a stack. Each new screen you navigate to means you’re adding a new screen on top of your stack. If you navigate backwards, you’re popping that screen off the stack.&lt;/li&gt;
&lt;li&gt;The other main thing to note is the use of URLs. In a web browser, you’re able to use URLs for navigation (making a specific server call to get HTML, which in turn pulls in CSS and JS). In mobile apps however, you’re not able to use a URL to navigate to a specific screen. (You can still use URLs behind the scenes to grab resources like images on the fly though.)&lt;/li&gt;
&lt;li&gt;URLs in the browser can also represent a navigational state within the web app (e.g. you can refresh the page and have the navigation state persist). This is not the usual behaviour in React Native apps, whereby refreshing the app will not persist navigation state by default.&lt;/li&gt;
&lt;li&gt;React Native has third-party packages that help with navigation, with a very common one being &lt;a href="https://reactnavigation.org/"&gt;React Navigation&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Deployment process
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Not going to lie, this is the part that I like the least about React Native, or more specifically, developing for mobile. Deployment of web apps is so easy nowadays. You can literally code up an app and deploy it for the entire world to see in under 10 minutes. Not so with mobile apps.&lt;/li&gt;
&lt;li&gt;The 2 main mobile operating systems are iOS and Android. Mobile apps for these platforms are distributed through the Apple App Store and Google Play Store respectively. You’ll therefore need to go through a review and approval process if you want to get your app listed on these stores, and these obviously will be two completely separate application processes.&lt;/li&gt;
&lt;li&gt;You’ll also need to go through this process every time you issue a native update to your app. You can release JavaScript-only updates via the Over-The-Air mechanism (OTA), which by-passes the app stores approval processes, making it infinitely simpler. Your users will get these updates whenever they reload the app (i.e. when the JavaScript bundle is updated).&lt;/li&gt;
&lt;li&gt;The deployment process is made more complicated if you also want to distribute a staging or beta app to app testers for QA testing before distributing to the app store. For this, you’ll likely want to use something like Firebase or AppCenter to distribute apps to a private testing group. You’ll therefore need to build different versions of your app, but also need to manage signing certificates for your iOS app to ensure this can be distributed. Fastlane is a tool that can help with managing this.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So that’s everything on my list that I can think of right now. If I’m missing anything, let me know, and I’ll add it to this list. 🙃 I'm at &lt;a href="https://bionicjulia.com"&gt;https://bionicjulia.com&lt;/a&gt; or &lt;a href="https://instagram.com/bionicjulia"&gt;https://instagram.com/bionicjulia&lt;/a&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>reactnative</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Github Actions Workflow For a React Native App</title>
      <dc:creator>Bionic Julia</dc:creator>
      <pubDate>Mon, 18 Apr 2022 00:00:00 +0000</pubDate>
      <link>https://forem.com/bionicjulia/github-actions-workflow-for-a-react-native-app-23ik</link>
      <guid>https://forem.com/bionicjulia/github-actions-workflow-for-a-react-native-app-23ik</guid>
      <description>&lt;p&gt;We had some downtime recently in the team, where I had just finished delivering a big feature and was in between builds. It was therefore a prime opportunity to tackle some technical debt, and one of the key things on our list was moving our Continuous Integration (CI) workflow to &lt;strong&gt;Github Actions&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;There were various reasons we wanted to move across to GH Actions, but a big one for us was the potential to cut costs, since our repo is public and open source (which qualifies us for free GH Actions minutes). It’s also nice to be able to keep our processes tight, limiting the number of third party tools our developers have to interact with, and reducing complexity and onboarding where possible (since we already use Github as a team).&lt;/p&gt;

&lt;p&gt;Writing CI config YAML files was not something I’ve had experience with in the past, so I volunteered to kick this off for the team. I’ll be honest - it was a pretty painful experience... but I’m really glad that I stuck with it and got our workflows working, as it’s given me a much better understanding of how our React Native app is built and deployed to our beta testers and app stores. The pain comes from having to deploy config changes and wait for the CI process to go through the various steps, only to watch it fail, so you can then make the change that you think will fix it, to then push and deploy again...wait for the CI process to restart again from scratch, only for it to fail at the next step, so you make the fix, that you then push and deploy again... etc etc. You get the point. It’s a slow and repetitive process, with a lot of trial and error (at least for me), as this was my first time doing this.&lt;/p&gt;

&lt;p&gt;Looking at the bigger picture, I think Github Actions is a great tool and would highly recommend all developers give it a try if they already push code to Github. It’s pretty easy to get a basic workflow integrated with your git environment, and worth seeing if GH Actions can help improve your developer experience by automating certain tasks and improving your checks and processes when deploying production code.&lt;/p&gt;

&lt;p&gt;I started with a simple workflow whereby I wanted all feature branches pushed to Github to automatically kick off the following tasks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Linting the code;&lt;/li&gt;
&lt;li&gt;Checking that all translations were complete; and&lt;/li&gt;
&lt;li&gt;Running Jest tests.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s what my workflow looks like.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Feature branch checks&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="c1"&gt;# Branch (Ignoring) [main]&lt;/span&gt;
&lt;span class="c1"&gt;# - Build app assets&lt;/span&gt;
&lt;span class="c1"&gt;# |-- Lint project&lt;/span&gt;
&lt;span class="c1"&gt;# |-- Check i18n string&lt;/span&gt;
&lt;span class="c1"&gt;# |-- Run Tests&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;**'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;!main'&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;setup&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Setup code and environment needed for linting, translations and tests&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;timeout-minutes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Check out Git repository&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v3&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Cache private assets&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/cache@v3&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cache-private-assets&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;.private-assets&lt;/span&gt;
            &lt;span class="s"&gt;assets/fonts&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;private-assets&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;webfactory/ssh-agent@v0.5.4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;ssh-private-key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.CSS_ASSETS_SSH_PRIVATE_KEY }}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install private assets if cache not present&lt;/span&gt;
        &lt;span class="na"&gt;shell&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bash&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.github/install_private_assets.sh&lt;/span&gt;
        &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;steps.cache-private-assets.outputs.cache-hit != 'true'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set up Node environment&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v2&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Cache node modules&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/cache@v3&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cache-node-modules&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node_modules&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yarn-${{ hashFiles('yarn.lock') }}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install node modules if cache not present&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yarn install --immutable&lt;/span&gt;
        &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;steps.cache-node-modules.outputs.cache-hit != 'true'&lt;/span&gt;

  &lt;span class="na"&gt;lint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;setup&lt;/span&gt;
    &lt;span class="na"&gt;timeout-minutes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Check out Git repository&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v3&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Restore private assets from cache&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/cache@v3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;.private-assets&lt;/span&gt;
            &lt;span class="s"&gt;assets/fonts&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;private-assets&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Restore node modules from cache&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/cache@v3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node_modules&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yarn-${{ hashFiles('yarn.lock') }}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mkdir -p ~/reports&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Lint&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yarn lint:ci&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Prettier&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yarn prettier&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Upload linting report&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/upload-artifact@v3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;lint-report&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;~/reports/eslint.xml&lt;/span&gt;

  &lt;span class="na"&gt;i18n&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;setup&lt;/span&gt;
    &lt;span class="na"&gt;timeout-minutes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Check out Git repository&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v3&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Restore private assets from cache&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/cache@v3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;.private-assets&lt;/span&gt;
            &lt;span class="s"&gt;assets/fonts&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;private-assets&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Restore node modules from cache&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/cache@v3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node_modules&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yarn-${{ hashFiles('yarn.lock') }}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Check Swedish translations&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yarn test:i18n 'sv-SE'&lt;/span&gt;

  &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;macos-latest&lt;/span&gt;
    &lt;span class="na"&gt;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;setup&lt;/span&gt;
    &lt;span class="na"&gt;timeout-minutes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Check out Git repository&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v3&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Restore private assets from cache&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/cache@v3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;.private-assets&lt;/span&gt;
            &lt;span class="s"&gt;assets/fonts&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;private-assets&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Restore node modules from cache&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/cache@v3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node_modules&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yarn-${{ hashFiles('yarn.lock') }}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run tests with JUnit as reporter&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yarn test:coverage&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;JEST_JUNIT_OUTPUT_DIR&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./reports/junit/&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Upload Jest JUnit test results&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/upload-artifact@v3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;jest-test-results&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./reports/junit/junit.xml&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you’re interested in reading more about GH Action’s workflows, check &lt;a href="https://docs.github.com/en/actions/learn-github-actions/understanding-github-actions"&gt;this out&lt;/a&gt;. To get this integrated into your Github workflow, the only thing you have to do is create a new directory in the root of your repo called &lt;code&gt;.github&lt;/code&gt;. In this directory, create a folder called &lt;code&gt;workflows&lt;/code&gt; and save the above file in there (calling it whatever you like). You can then go to your Github repo settings and enable Github Actions...and that’s it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Defining when the workflow runs
&lt;/h3&gt;

&lt;p&gt;This specific workflow will automatically run whenever we push a branch to Github that is NOT called &lt;code&gt;main&lt;/code&gt;. This is set with this particular configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;**'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;!main'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Defining the jobs
&lt;/h3&gt;

&lt;p&gt;The workflow then goes on to say that we want to run 3 jobs simultaneously (&lt;code&gt;lint&lt;/code&gt;, &lt;code&gt;i18n&lt;/code&gt; and &lt;code&gt;test&lt;/code&gt;), all 3 of which requires an initial job called &lt;code&gt;setup&lt;/code&gt; to run. You’ll need to define an operating system runner for each of your jobs e.g. &lt;code&gt;ubuntu&lt;/code&gt; or &lt;code&gt;macOS&lt;/code&gt; (if you can, run your jobs on ubuntu as the minutes on Github are cheaper!). You can also define specific versions to use, but in my case, I just used the latest versions.&lt;/p&gt;

&lt;p&gt;You can then optionally define how long the job should run for before it times out. It’s probably a good idea to include this in case something goes wrong, so you don’t end up wasting minutes on tasks which might (for instance) end up in some kind of erroneous infinite loop.&lt;/p&gt;

&lt;h3&gt;
  
  
  Defining the steps within a job
&lt;/h3&gt;

&lt;p&gt;Going back to the &lt;code&gt;setup&lt;/code&gt; job defined above, this does a checkout of our repo’s code, installs the necessary dependencies and caches it for subsequent jobs. In my case, we had some private assets and fonts our app needed access to as part of the build process, which in turn required SSH access to a private Github repo.&lt;/p&gt;

&lt;p&gt;One of the nice things about GH Actions is the prebuilt actions that other community members have coded up, which more likely that not, will cover whatever you’re looking to do. Here’s the &lt;a href="https://github.com/marketplace?type=actions"&gt;marketplace&lt;/a&gt; if you want to have a browse. As an example, I used this community action &lt;code&gt;webfactory/ssh-agent@v0.5.4&lt;/code&gt;, to help me SSH with a private key, into our private repo. I then ran a custom script called &lt;code&gt;install_private_assets.sh&lt;/code&gt; to install what I needed. I could’ve coded up the steps manually myself, but this saved me some hassle and made our workflow file easy to read and follow. Just a note here that you’ll likely want to delve into the details of a community-defined action before using it, just to verify that it’s not doing anything nefarious under the hood (like mining bitcoin!). 😝&lt;/p&gt;

&lt;p&gt;The safer bet would be to use official actions provided by Github, where possible, which can be easily identified as their names start with &lt;code&gt;actions/&lt;/code&gt;, e.g. &lt;code&gt;actions/cache@v3&lt;/code&gt; (which is an action that helps us with caching logic). Each action will come with instructions on what parameters you need to pass in to get it to work.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Cache private assets&lt;/span&gt;
  &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/cache@v3&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cache-private-assets&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
      &lt;span class="s"&gt;.private-assets&lt;/span&gt;
      &lt;span class="s"&gt;assets/fonts&lt;/span&gt;
    &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;private-assets&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;webfactory/ssh-agent@v0.5.4&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;ssh-private-key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.CSS_ASSETS_SSH_PRIVATE_KEY }}&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install private assets if cache not present&lt;/span&gt;
  &lt;span class="na"&gt;shell&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bash&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.github/install_private_assets.sh&lt;/span&gt;
  &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;steps.cache-private-assets.outputs.cache-hit != 'true'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To enable the workflow to be read intuitively from top to bottom, I named the various steps in each job with a descriptive name, using &lt;code&gt;name:&lt;/code&gt;. In essence, the pattern for each step in a job goes like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Define the name of the step (optional).&lt;/li&gt;
&lt;li&gt;Define the specific Github / community defined &lt;strong&gt;action&lt;/strong&gt; you need with &lt;code&gt;uses:&lt;/code&gt;. Alternatively, you can use &lt;code&gt;run:&lt;/code&gt; if there’s a &lt;strong&gt;script&lt;/strong&gt; &lt;strong&gt;command&lt;/strong&gt; you want to run instead (e.g. &lt;code&gt;yarn test:coverage&lt;/code&gt; kicks off our Jest tests as defined in our &lt;code&gt;package.json&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Pass in the configuration or params for the action, if any, using &lt;code&gt;with:&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Check out Git repository&lt;/span&gt;
    &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v3&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Restore private assets from cache&lt;/span&gt;
    &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/cache@v3&lt;/span&gt;
    &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
        &lt;span class="s"&gt;.private-assets&lt;/span&gt;
        &lt;span class="s"&gt;assets/fonts&lt;/span&gt;
      &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;private-assets&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you need to pass in multiple arguments, use the &lt;code&gt;|&lt;/code&gt; e.g.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
  &lt;span class="s"&gt;.private-assets&lt;/span&gt;
  &lt;span class="s"&gt;assets/fonts&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Workflow for building and deploying a React Native production app
&lt;/h3&gt;

&lt;p&gt;If you’ve reached this point...thanks for sticking with me! As a bonus below, I’ve added another workflow I put together. This is the one that’s kicked off whenever anyone in the team deploys code to our production branch (&lt;code&gt;main&lt;/code&gt;), which allows for a release via OTA, or a build and deploy to the Apple and Google app stores with Fastlane.&lt;/p&gt;

&lt;p&gt;You might have noticed the reference to a &lt;code&gt;secrets&lt;/code&gt; object in my various workflows e.g.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;webfactory/ssh-agent@v0.5.4&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;ssh-private-key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.CSS_ASSETS_SSH_PRIVATE_KEY }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These are essentially environment variables you can set in the Github user interface, within the Settings page of your repo. In the example below, you’ll note that &lt;code&gt;${{ secrets.CSS_ASSETS_SSH_PRIVATE_KEY }}&lt;/code&gt; is called from within the &lt;code&gt;deploy-prod-ota&lt;/code&gt; job that runs in &lt;code&gt;environment: production-ota&lt;/code&gt;. This requires that I have an environment setup in Github called &lt;code&gt;production-ota&lt;/code&gt;, within which I can store my environment secrets. Alternatively, if you’ll be sharing secrets across environments, you can also store these secrets at a repository level.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Production deployment via main branch&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;main'&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;deploy-prod-ota&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy production OTA&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;macos-latest&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;production-ota&lt;/span&gt;
    &lt;span class="na"&gt;timeout-minutes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;15&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Check out Git repository&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v3&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Cache private assets&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/cache@v3&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cache-private-assets&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;.private-assets&lt;/span&gt;
            &lt;span class="s"&gt;assets/fonts&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;private-assets&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;webfactory/ssh-agent@v0.5.4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;ssh-private-key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.CSS_ASSETS_SSH_PRIVATE_KEY }}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install private assets if cache not present&lt;/span&gt;
        &lt;span class="na"&gt;shell&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bash&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.github/install_private_assets.sh&lt;/span&gt;
        &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;steps.cache-private-assets.outputs.cache-hit != 'true'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Cache gems&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cache-gems&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/cache@v3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vendor/bundle&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gems-${{ hashFiles('**/Gemfile.lock') }}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install bundler if cache not present&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gem install bundler&lt;/span&gt;
        &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;steps.cache-gems.outputs.cache-hit != 'true'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install gems if cache not present&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;bundle config path vendor/bundle clean 'true'&lt;/span&gt;
          &lt;span class="s"&gt;bundle check || bundle install&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;BUNDLE_JOBS&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;4'&lt;/span&gt;
          &lt;span class="na"&gt;BUNDLE_RETRY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3'&lt;/span&gt;
        &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;steps.cache-gems.outputs.cache-hit != 'true'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set up Node environment&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v2&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Cache node modules&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/cache@v3&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cache-node-modules&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node_modules&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yarn-${{ hashFiles('yarn.lock') }}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install node modules if cache not present&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yarn install --immutable&lt;/span&gt;
        &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;steps.cache-node-modules.outputs.cache-hit != 'true'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Prepare deploy environment&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;bundle update fastlane&lt;/span&gt;
          &lt;span class="s"&gt;bundle exec fastlane update_plugins&lt;/span&gt;
          &lt;span class="s"&gt;bash .github/make_deploy_env.sh&lt;/span&gt;
          &lt;span class="s"&gt;yarn generate:git-hash&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;RELEASE_TYPE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.RELEASE_TYPE }}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;echo "RELEASE_CHANNEL=$(grep EXPO_RELEASE_CHANNEL .env | cut -d '=' -f2)" &amp;gt;&amp;gt; $GITHUB_ENV&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Expo OTA login&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npx expo-cli login -u ${{ secrets.EXPO_USERNAME }} -p ${{ secrets.EXPO_PASSWORD }}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Expo OTA publish&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npx expo-cli publish --non-interactive --max-workers 1 --release-channel ${{ env.RELEASE_CHANNEL }} --target bare&lt;/span&gt;

  &lt;span class="c1"&gt;#&lt;/span&gt;
  &lt;span class="c1"&gt;# Publish app to store&lt;/span&gt;
  &lt;span class="c1"&gt;# - App / Store release through AppCenter (with approval)&lt;/span&gt;
  &lt;span class="c1"&gt;# |-- Step 1: Setup production app build (approval needed)&lt;/span&gt;
  &lt;span class="c1"&gt;#   |-- Step 2a: iOS Release to app store (through AppCenter)&lt;/span&gt;
  &lt;span class="c1"&gt;#   |-- Step 2b: Android Release to play store (through AppCenter)&lt;/span&gt;
  &lt;span class="c1"&gt;#&lt;/span&gt;

  &lt;span class="na"&gt;setup-prod-release&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Setup production app build&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;macos-latest&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;production-build-setup&lt;/span&gt;
    &lt;span class="na"&gt;timeout-minutes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;15&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Check out Git repository&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v3&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Cache private assets&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/cache@v3&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cache-private-assets&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;.private-assets&lt;/span&gt;
            &lt;span class="s"&gt;assets/fonts&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;private-assets&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;webfactory/ssh-agent@v0.5.4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;ssh-private-key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.CSS_ASSETS_SSH_PRIVATE_KEY }}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install private assets if cache not present&lt;/span&gt;
        &lt;span class="na"&gt;shell&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bash&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.github/install_private_assets.sh&lt;/span&gt;
        &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;steps.cache-private-assets.outputs.cache-hit != 'true'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Cache gems&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cache-gems&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/cache@v3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vendor/bundle&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gems-${{ hashFiles('**/Gemfile.lock') }}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install bundler if cache not present&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gem install bundler&lt;/span&gt;
        &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;steps.cache-gems.outputs.cache-hit != 'true'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install gems if cache not present&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;bundle config path vendor/bundle clean 'true'&lt;/span&gt;
          &lt;span class="s"&gt;bundle check || bundle install&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;BUNDLE_JOBS&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;4'&lt;/span&gt;
          &lt;span class="na"&gt;BUNDLE_RETRY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3'&lt;/span&gt;
        &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;steps.cache-gems.outputs.cache-hit != 'true'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set up Node environment&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v2&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Cache node modules&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/cache@v3&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cache-node-modules&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node_modules&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yarn-${{ hashFiles('yarn.lock') }}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install node modules if cache not present&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yarn install --immutable&lt;/span&gt;
        &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;steps.cache-node-modules.outputs.cache-hit != 'true'&lt;/span&gt;

  &lt;span class="na"&gt;prod-ios-release&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Production iOS app build and release&lt;/span&gt;
    &lt;span class="na"&gt;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;setup-prod-release&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;macos-latest&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;production-release&lt;/span&gt;
    &lt;span class="na"&gt;timeout-minutes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;30&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Check out Git repository&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v3&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Restore private assets from cache&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/cache@v3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;.private-assets&lt;/span&gt;
            &lt;span class="s"&gt;assets/fonts&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;private-assets&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Restore gems from cache&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/cache@v3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vendor/bundle&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gems-${{ hashFiles('**/Gemfile.lock') }}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Restore node modules from cache&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/cache@v3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node_modules&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yarn-${{ hashFiles('yarn.lock') }}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Prepare deploy environment&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;bundle update fastlane&lt;/span&gt;
          &lt;span class="s"&gt;bundle exec fastlane update_plugins&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;webfactory/ssh-agent@v0.5.4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;ssh-private-key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.IOS_CERTS_SSH_PRIVATE_KEY }}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Make deploy environment&lt;/span&gt;
        &lt;span class="na"&gt;shell&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bash&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.github/make_deploy_env.sh&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;RELEASE_TYPE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.RELEASE_TYPE }}&lt;/span&gt;
          &lt;span class="na"&gt;NAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.NAME }}&lt;/span&gt;
          &lt;span class="c1"&gt;# ... other env vars&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yarn generate:git-hash&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Pod clean and install&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;cd ios &amp;amp;&amp;amp; pod install --clean-install&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Generate fastlane certificates&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yarn fastlane:ios:certificates&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Fastlane ios release&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yarn fastlane:ios:release env:$RELEASE_TYPE&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;RELEASE_TYPE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.RELEASE_TYPE }}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Upload build&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/upload-artifact@v3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;production-ios-build&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./build&lt;/span&gt;

  &lt;span class="na"&gt;prod-android-release&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Production Android app build and release&lt;/span&gt;
    &lt;span class="na"&gt;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;setup-prod-release&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;macos-latest&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;production-release&lt;/span&gt;
    &lt;span class="na"&gt;timeout-minutes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;30&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Check out Git repository&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v3&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Restore private assets from cache&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/cache@v3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;.private-assets&lt;/span&gt;
            &lt;span class="s"&gt;assets/fonts&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;private-assets&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Restore gems from cache&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/cache@v3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vendor/bundle&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gems-${{ hashFiles('**/Gemfile.lock') }}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Restore node modules from cache&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/cache@v3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node_modules&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yarn-${{ hashFiles('yarn.lock') }}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Bundle RN Android assets&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npx react-native bundle --platform android --dev &lt;/span&gt;&lt;span class="no"&gt;false&lt;/span&gt;&lt;span class="s"&gt; --entry-file index.js --bundle-output android/app/src/main/assets/index.android.bundle --assets-dest android/app/src/main/res&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Clean up after bundling&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rm -rf ./android/app/src/main/res/drawable-* &amp;amp;&amp;amp; rm -rf ./android/app/src/main/res/raw&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Prepare deploy environment&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;bundle update fastlane&lt;/span&gt;
          &lt;span class="s"&gt;bundle exec fastlane update_plugins&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Make deploy environment&lt;/span&gt;
        &lt;span class="na"&gt;shell&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bash&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.github/make_deploy_env.sh&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;RELEASE_TYPE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.RELEASE_TYPE }}&lt;/span&gt;
          &lt;span class="na"&gt;NAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.NAME }}&lt;/span&gt;
          &lt;span class="c1"&gt;# ... other env vars&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yarn generate:git-hash&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;bundle config path vendor/bundle clean 'true'&lt;/span&gt;
          &lt;span class="s"&gt;bundle install &amp;amp;&amp;amp; cat android/gradle.properties&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Fastlane android release&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ENVFILE=.env bundle exec fastlane android release env:$RELEASE_TYPE&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;RELEASE_TYPE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.RELEASE_TYPE }}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Upload build&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/upload-artifact@v3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;production-android-build&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./android/app/build/outputs&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If this helped you in anyway, I’d love to hear about it. If you have any suggestions on how I might improve this post, please do also let me know. I'm at &lt;a href="https://bionicjulia.com"&gt;https://bionicjulia.com&lt;/a&gt;, &lt;a href="https://twitter.com/bionicjulia"&gt;Twitter&lt;/a&gt; and &lt;a href="https://instagram.com/bionicjulia"&gt;Instagram&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>reactnative</category>
      <category>devtools</category>
      <category>githubactions</category>
      <category>ci</category>
    </item>
    <item>
      <title>Improving Lighthouse Scores For bionicjulia.com</title>
      <dc:creator>Bionic Julia</dc:creator>
      <pubDate>Sat, 05 Mar 2022 00:00:00 +0000</pubDate>
      <link>https://forem.com/bionicjulia/improving-lighthouse-scores-for-bionicjuliacom-2mbp</link>
      <guid>https://forem.com/bionicjulia/improving-lighthouse-scores-for-bionicjuliacom-2mbp</guid>
      <description>&lt;p&gt;Carrying on from my &lt;a href="https://dev.to/blog/accessibility-tips-for-web-development"&gt;accessibility post&lt;/a&gt; last week, I thought I’d spend some time putting knowledge to practice. I spent my morning looking into &lt;a href="https://developers.google.com/web/tools/lighthouse"&gt;Chrome’s Lighthouse&lt;/a&gt; feature and worked at improving my website to see what differences it would make to my Lighthouse scores.&lt;/p&gt;

&lt;h2&gt;
  
  
  Starting point
&lt;/h2&gt;

&lt;p&gt;Though the main landing page of my website is &lt;a href="https://dev.to/"&gt;bionicjulia.com&lt;/a&gt;, I thought I’d try to optimise &lt;a href="https://dev.to/blog"&gt;bionicjulia.com/blog&lt;/a&gt; as this is the page that I want people to visit the most. Here are my starting scores - not &lt;em&gt;too&lt;/em&gt; bad, but the accessibility score could definitely do with a boost.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Hw0BIMDV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/7389546/156882641-975e919f-d248-483b-806d-1012fddb57e4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Hw0BIMDV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/7389546/156882641-975e919f-d248-483b-806d-1012fddb57e4.png" alt="Summary scores before improvements" width="880" height="320"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Performance
&lt;/h3&gt;

&lt;p&gt;The really nice thing about Lighthouse is the detailed suggestions it provides on how to improve your scores. If we look at the “Performance” score, it was mainly the “total blocking time” that was letting me down. Delving into it further, the diagnostics showed there were a number of long main-thread tasks being run, which was useful in identifying the worst contributors to input delay. You can read more about what causes this &lt;a href="https://web.dev/long-tasks-devtools"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ykn7R0Hi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/7389546/156882673-b98005ef-4a10-4b84-b78c-c09a7391de7b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ykn7R0Hi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/7389546/156882673-b98005ef-4a10-4b84-b78c-c09a7391de7b.png" alt="Performance score before improvements" width="880" height="977"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Accessibility
&lt;/h3&gt;

&lt;p&gt;I found the accessibility section to be extremely useful. There are relevant external links to help you understand more about what makes a site accessible in general, but also very specific improvement points for your site. Check out the snapshot below to see what I mean - each suggestion can be expanded to give you even more precise information on where in your code base, the problems were identified.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lMyTIQcO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/7389546/156882686-e8398ade-935a-43e6-bd45-274628d82ccd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lMyTIQcO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/7389546/156882686-e8398ade-935a-43e6-bd45-274628d82ccd.png" alt="Accessibility scores before improvements" width="880" height="1209"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  After the improvements were made...
&lt;/h2&gt;

&lt;p&gt;I use NextJS and Vercel to serve up &lt;a href="https://dev.to/"&gt;bionicjulia.com&lt;/a&gt;. Some of the suggestions on how to optimise my site was therefore out of my control, but I tried my best to improve as much as I could. Here are my post-improvement Lighthouse scores.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9Lx_ln8N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/7389546/156882729-7b621193-7e71-4dc6-9567-13c3c5c169c3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9Lx_ln8N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/7389546/156882729-7b621193-7e71-4dc6-9567-13c3c5c169c3.png" alt="Summary scores after improvements" width="880" height="277"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Not bad at all (if I may say so myself!), considering how quick and easy it was to make the fixes. I’m somewhat gutted that my performance score isn’t in the green (I need to dig deeper into NextJS if I want to improve this further), but I’m very pleased with my accessibility score improvement.&lt;/p&gt;

&lt;h3&gt;
  
  
  Performance
&lt;/h3&gt;

&lt;p&gt;The improvement in my performance score does seem to have been led by the decrease in total blocking time and time to interactive. This was mostly achieved by reducing the size of some of my images and scripts.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ohjfmY3g--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/7389546/156882742-85912af0-f024-4bb6-a1d5-f5914d9309ed.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ohjfmY3g--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/7389546/156882742-85912af0-f024-4bb6-a1d5-f5914d9309ed.png" alt="Performance scores after improvements" width="880" height="769"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Accessibility
&lt;/h3&gt;

&lt;p&gt;For accessibility, I made all of the suggested changes (bar one which didn’t work for the way I had set up my website), which were mostly around:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ensuring any links that used SVGs instead of text, had ARIA labels specifying what the link related to for screen readers.&lt;/li&gt;
&lt;li&gt;Ensuring that my unordered lists had child &lt;code&gt;&amp;lt;li&amp;gt;&lt;/code&gt; tags.&lt;/li&gt;
&lt;li&gt;Improving the colour contrast ratio of my links.&lt;/li&gt;
&lt;li&gt;Basic HTML syntax improvements.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_NkAKMhe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/7389546/156882759-cd8f172d-5ea2-482d-b479-bf4b13de1d98.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_NkAKMhe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/7389546/156882759-cd8f172d-5ea2-482d-b479-bf4b13de1d98.png" alt="Accessibility scores after improvements" width="880" height="399"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Though I didn’t set out to optimise the main landing page at &lt;a href="http://bionicjulia.com"&gt;bionicjulia.com&lt;/a&gt;, the improvements I made also affected my index page...where I’m pleased to say I got top marks. 🎉&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uok748pr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/7389546/156882772-f2ad5fef-bd65-4913-883a-5a6f140adca2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uok748pr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/7389546/156882772-f2ad5fef-bd65-4913-883a-5a6f140adca2.png" alt="Summary scores for landing page" width="880" height="280"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There’s definitely still room for improvement though, and next steps would be to run this analysis on each of the individual pages of my site. Lighthouse is also only one such tool, but it’s a good start. 🤓&lt;/p&gt;

&lt;p&gt;If you have any suggestions on how I might improve this post, please do also let me know. I'm at &lt;a href="https://bionicjulia.com"&gt;https://bionicjulia.com&lt;/a&gt;, &lt;a href="https://twitter.com/bionicjulia"&gt;Twitter&lt;/a&gt; and &lt;a href="https://instagram.com/bionicjulia"&gt;Instagram&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>a11y</category>
      <category>nextjs</category>
      <category>devtools</category>
      <category>seo</category>
    </item>
    <item>
      <title>Accessibility Tips For Web Development</title>
      <dc:creator>Bionic Julia</dc:creator>
      <pubDate>Fri, 25 Feb 2022 00:00:00 +0000</pubDate>
      <link>https://forem.com/bionicjulia/accessibility-tips-for-web-development-4abo</link>
      <guid>https://forem.com/bionicjulia/accessibility-tips-for-web-development-4abo</guid>
      <description>&lt;p&gt;One of my knowledge gaps within web development that I really want to bridge is accessibility. In all of the apps / engineering teams I’ve worked on professionally to date, accessibility (unfortunately) has never played a front and centre stage, and tends to be more of an afterthought. This is mostly due to time and resource constraints, but also a lack of knowledge on how to implement accessible apps and the benefits it brings.&lt;/p&gt;

&lt;p&gt;To start making steps to improve my knowledge, I recently decided to take an online course on accessibility for web development. Here are my quick notes and key takeaways, written in a cheat sheet format (mostly for me to reference back to at a later date!). If you’re knowledgeable about accessible web development, and think I’ve missed something crucial out, please do let me know, and I’ll add it to the list.&lt;/p&gt;

&lt;h3&gt;
  
  
  Screen readers
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Add alt text to images, otherwise screen readers will end up reading the filename.&lt;/li&gt;
&lt;li&gt;Empty strings as alt tags will cause the screen reader to skip over the image (which may be the desired effect you want, e.g. for user uploaded images or decorative images).&lt;/li&gt;
&lt;li&gt;For complex images, you could make the image hidden to screen readers, but add some text below, that’s visually hidden, but readable by screen readers.&lt;/li&gt;
&lt;li&gt;If you have a video, add captions. At &lt;a href="https://www.levelaccess.com/accessibility-regulations/accessibility-standards/"&gt;WCAG2 Level A&lt;/a&gt;, you can include this as a transcript. To improve on this, use synchronised captions.&lt;/li&gt;
&lt;li&gt;Screen readers ignore CSS background images.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;💡 In Mac OS, the screen reader can be activated via the VoiceOver settings.&lt;/p&gt;

&lt;h3&gt;
  
  
  Accessible HTML
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Use appropriate semantic markup. Some have no special functionality, but others do, like &lt;code&gt;button&lt;/code&gt;, &lt;code&gt;input&lt;/code&gt; and &lt;code&gt;textarea&lt;/code&gt;.

&lt;ul&gt;
&lt;li&gt;e.g. if you just use &lt;code&gt;div&lt;/code&gt;, this would not be read by a screen reader.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Don’t choose which header tags to use based on formatting! e.g. there should only be one &lt;code&gt;h1&lt;/code&gt; tag per page.&lt;/li&gt;
&lt;li&gt;Ensure that the reading and navigation order (determined by code order) is logical (so it makes sense when read by a screen reader from top to bottom).&lt;/li&gt;
&lt;li&gt;Use form field labels, so that form inputs can be read by screen readers. Labels can be used explicitly (use &lt;code&gt;for&lt;/code&gt; to link it to a particular input name) or implicitly (wraps the name and input field - does not need &lt;code&gt;for&lt;/code&gt; attribute).&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;label&lt;/code&gt; tag only works with certain elements (e.g. &lt;code&gt;input&lt;/code&gt;, &lt;code&gt;button&lt;/code&gt;).

&lt;ul&gt;
&lt;li&gt;If you need to label an element that’s not “label-able”, use &lt;code&gt;aria-label&lt;/code&gt; attribute instead (however note that it would only work on focusable elements, so would also require a &lt;code&gt;tabindex&lt;/code&gt; attribute).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Use a specific class to visually hide an element that’s solely for screen reader only content.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;  &lt;span class="nc"&gt;.visuallyhidden&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;absolute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;-500px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;overflow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;hidden&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;If you choose not to use the &lt;code&gt;button&lt;/code&gt; element, and instead to use a &lt;code&gt;div&lt;/code&gt; styled as a button, you’d also need to include the following attributes to make it accessible:

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;onclick=”xxx”&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;role=”button”&lt;/code&gt; (this is the ARIA role, used by screen readers)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tabindex=”0”&lt;/code&gt; (or whatever number, to make it tab-able and focusable with the keyboard)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;onkeyup=”xxx”&lt;/code&gt; (to ensure keyboard-only users can interact with the button)&lt;/li&gt;
&lt;/ul&gt;


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

&lt;h3&gt;
  
  
  ARIA - Accessible Rich Internet Applications
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Labels

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;aria-label&lt;/code&gt; - inline attribute&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;aria-labelledby&lt;/code&gt; - allows you to link to an element id that contains the text for the label e.g. could be handy for handling translations&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;aria-describedby&lt;/code&gt; - provides extended information the user might need&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Roles can be applied to any element. Role examples include button, checkbox, tree, banner.&lt;/li&gt;
&lt;li&gt;States and properties inform what the current state of an element / app is in e.g. is a checkbox checked, or is a form invalid? These would need to be added and removed in line with the user action.&lt;/li&gt;
&lt;li&gt;You can use ARIA properties within CSS since they are just data selectors.&lt;/li&gt;
&lt;li&gt;The ARIA spec allows us to mark an element as containing live data so that screen readers can read out live updates as they come.

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;assertive&lt;/code&gt; means interrupt, &lt;code&gt;polite&lt;/code&gt; announces the update when the screen reader is next idle. &lt;code&gt;off&lt;/code&gt; will not read the update.&lt;/li&gt;
&lt;li&gt;This kicks in whenever the content between the div tag changes.
&lt;/li&gt;
&lt;/ul&gt;


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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;aria-live=&lt;/span&gt;&lt;span class="s"&gt;"assertive"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Waiting for a ride&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Focus management
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Keyboard shortcuts - allow users to access different features through using the keyboard alone.&lt;/li&gt;
&lt;li&gt;Keyboard only users - show focus rings around elements which are also safe for screen readers. Should be able to reach the next focusable element by tabbing.

&lt;ul&gt;
&lt;li&gt;Also should not result in a substantial change in the page layout e.g. page shifts because of the focus ring designs.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Skip links help users skip over large headers and navigation, to go straight to the main content of the site (without having to tab through each link).

&lt;ul&gt;
&lt;li&gt;Create an anchor within the body and add it to the start of your HTML content. Make it visually hidden, until someone hits tab (and make it immediately in focus).&lt;/li&gt;
&lt;li&gt;The anchor should then link to the main content.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Accessible tab navigation allows a user to navigate to the next tabbable item. &lt;code&gt;Shift + tab&lt;/code&gt; allows a user to navigate back.

&lt;ul&gt;
&lt;li&gt;Tabbable elements out of the box include: &lt;code&gt;a&lt;/code&gt;, &lt;code&gt;button&lt;/code&gt;, &lt;code&gt;input&lt;/code&gt;, &lt;code&gt;select&lt;/code&gt;, &lt;code&gt;textarea&lt;/code&gt;, &lt;code&gt;iframe&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Use a &lt;code&gt;tabindex&lt;/code&gt; attribute to make any element tabbable

&lt;ul&gt;
&lt;li&gt;A negative value means it should be focusable, but not reachable via sequential keyboard navigation.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;0&lt;/code&gt; means the element should be focusable and reachable, but its relative order is defined by the platform convention.&lt;/li&gt;
&lt;li&gt;A positive value means it should be focusable and reachable. Its relative order is defined by the value of the attribute. The higher the value, the earlier it’s reached.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Note, top level elements should be tabbable, but child elements should be arrow-key reachable e.g. if you have a tab navigation, with “more” as one of the tabs, the child links within “more” should not be accessed via the tab key, but arrow keys.&lt;/li&gt;
&lt;li&gt;You can get the DOM’s current active element by using &lt;code&gt;document.activeElement&lt;/code&gt;. This is useful to store the currently focused element before a page transition so you can return the user to it later.&lt;/li&gt;
&lt;li&gt;Tab trapping is a JS technique where you grab the first and last element in a modal, and listen to keydown events for when the user is on the first/last element. You can then ensure that the tab and shift+tab takes the user through a loop of the tabbable elements in the modal (otherwise the user might tab out of the modal to the main document, even whilst the modal is open).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Visual considerations
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Use this website to check colour contrast: &lt;a href="https://webaim.org/resources/contrastchecker/"&gt;https://webaim.org/resources/contrastchecker/&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;Lighthouse audit (Chrome extension) can also check your live site.&lt;/li&gt;
&lt;li&gt;Chrome devtools’s colour picker shows the contrast ratio of two colours, however, this only works if you’ve set the child within a parent component (rather than against the document body).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;You shouldn’t just rely on colour to relay feedback back to the user (e.g. with form validation). There should also be some other method of feedback, like text.&lt;/li&gt;
&lt;li&gt;Consider using NoCoffee for Firefox to simulate what visual impairment views might look like.&lt;/li&gt;
&lt;li&gt;Consider keeping related items (especially those that provide feedback to user interactions) close together to help users with limited field of vision.&lt;/li&gt;
&lt;li&gt;Set the &lt;code&gt;lang&lt;/code&gt; attribute (the language of your site) at the top of your HTML file (and any subsequent sections where the language deviates from the default).&lt;/li&gt;
&lt;li&gt;Even though it might not be visually apparent, WCAG also specifies you should ensure your HTML is free of syntax and parsing errors.&lt;/li&gt;
&lt;li&gt;Users can set a “prefers reduced motion” option on their operating system. This is important for users who may suffer from seizures.

&lt;ul&gt;
&lt;li&gt;This is accessible within CSS via an &lt;code&gt;@media (prefers-reduced-motion)&lt;/code&gt; query.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Users can also set their system’s preferred colour scheme (i.e. light vs dark colour scheme).

&lt;ul&gt;
&lt;li&gt;This is accessible within CSS via an &lt;code&gt;@media (prefers-color-scheme)&lt;/code&gt; query.&lt;/li&gt;
&lt;/ul&gt;


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

&lt;h3&gt;
  
  
  Tooling
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Use an IDE linter on your code base e.g. &lt;a href="https://www.npmjs.com/package/eslint-plugin-jsx-a11y"&gt;eslint-plugin-a11y&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Use an accessible design system, e.g. from Adobe React Spectrum, Bootstrap, Google’s Material Design&lt;/li&gt;
&lt;li&gt;Developer tools from Deque: &lt;a href="https://www.deque.com/axe/devtools/"&gt;https://www.deque.com/axe/devtools/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Lighthouse: &lt;a href="https://developers.google.com/web/tools/lighthouse"&gt;https://developers.google.com/web/tools/lighthouse&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If this helped you in anyway, I’d love to hear about it. If you have any suggestions on how I might improve this post, please do also let me know. I'm at &lt;a href="https://bionicjulia.com"&gt;https://bionicjulia.com&lt;/a&gt;, &lt;a href="https://twitter.com/bionicjulia"&gt;Twitter&lt;/a&gt; and &lt;a href="https://instagram.com/bionicjulia"&gt;Instagram&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>accessibilty</category>
    </item>
    <item>
      <title>Creating a Sentry Logging Module to Set Context in a React Native App</title>
      <dc:creator>Bionic Julia</dc:creator>
      <pubDate>Sat, 19 Feb 2022 00:00:00 +0000</pubDate>
      <link>https://forem.com/bionicjulia/creating-a-sentry-logging-module-to-set-context-in-a-react-native-app-2ff3</link>
      <guid>https://forem.com/bionicjulia/creating-a-sentry-logging-module-to-set-context-in-a-react-native-app-2ff3</guid>
      <description>&lt;p&gt;We experienced a real puzzler of a bug last week. We had just released a massive new feature, and over the next two days, had gotten a number of users emailing in saying they couldn’t proceed at a certain point of the flow (a button that should have been pressable remained disabled). Looking at the analytics, the data agreed with what the emailers were saying, and the numbers experiencing the issue were not insignificant.&lt;/p&gt;

&lt;p&gt;Unfortunately, when we set about to solve the issue, no one in our engineering team, or the wider team could replicate the bug. Reading through the code didn’t surface any obvious errors either, and we were stumped. So what’s one to do next? 🤔&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Get more information.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Our app’s logging capabilities up until this point had been woeful. We were tracking app errors with &lt;a href="https://sentry.io"&gt;Sentry&lt;/a&gt; but nothing much else besides that. This meant that we were not able to surface any information to do with our bug above, as the app was not crashing per se. I therefore created a custom logging module to allow us to raise custom errors in Sentry, wherever we needed to understand the context around why users might be experiencing certain issues (rather than just relying on Sentry’s automatic error tracking when the app crashes).&lt;/p&gt;

&lt;p&gt;Here’s what my logging module looks like. Note that it uses the concept of Sentry’s custom scope and context which you can read more about &lt;a href="https://docs.sentry.io/platforms/react-native/enriching-events/scopes/"&gt;here&lt;/a&gt;. I found the official documents a little confusing, and it took a couple of tries before I started to see my custom logs show up, so I thought I’d write this up for others who might also be struggling.&lt;/p&gt;

&lt;h2&gt;
  
  
  Logging module
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Sentry&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@sentry/browser&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kr"&gt;enum&lt;/span&gt; &lt;span class="nx"&gt;EFeature1Context&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;EXAMPLE_CONTEXT_1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;EXAMPLE_CONTEXT_1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;EXAMPLE_CONTEXT_2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;EXAMPLE_CONTEXT_2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kr"&gt;enum&lt;/span&gt; &lt;span class="nx"&gt;EFeature2Context&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;EXAMPLE_CONTEXT_3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;EXAMPLE_CONTEXT_3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;contextNames&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;EFeature1Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;EFeature2Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;TContextName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;contextNames&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;TFeatureName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;feature_1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;feature_2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;logLevel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Sentry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Severity&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;featureName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TFeatureName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;errorMessage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;contextName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TContextName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;level&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Sentry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Severity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Sentry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Severity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;Sentry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;contextName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="nx"&gt;Sentry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;withScope&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setTag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;feature&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;featureName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setLevel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;level&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;Sentry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;captureException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;errorMessage&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The route I went down was to create a &lt;code&gt;log&lt;/code&gt; method, that takes a number of arguments, which are passed into Sentry’s &lt;code&gt;setContext&lt;/code&gt; and &lt;code&gt;withScope&lt;/code&gt; methods. The idea is to first set up the Sentry context which includes an object which you can stuff with whatever information you need for debugging. I then created a custom scope to set a tag (making it easily searchable within Sentry), the error level (e.g. info, warning, error, critical etc. which you can get from the &lt;code&gt;Sentry.Severity&lt;/code&gt; list that comes inbuilt with the Sentry package) and the error message.&lt;/p&gt;

&lt;p&gt;Note that the separation of context names into different enums is for convenience and helping to keep track of the various contexts you might be setting if your app is a large one, and you’re using Typescript.&lt;/p&gt;

&lt;p&gt;To use the Logging module in your app, you’d just need to add this line of code wherever you want to log additional context.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;Logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;feature_1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;example_error_message&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;contextNames&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;EXAMPLE_CONTEXT_1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;user_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;abc&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;country&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;uk&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;screen_name_example&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nx"&gt;logLevel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If this helped you in anyway, I’d love to hear about it. If you have any suggestions on how I might improve this post, please do also let me know. I'm at &lt;a href="https://bionicjulia.com"&gt;https://bionicjulia.com&lt;/a&gt;, &lt;a href="https://twitter.com/bionicjulia"&gt;Twitter&lt;/a&gt; and &lt;a href="https://instagram.com/bionicjulia"&gt;Instagram&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>reactnative</category>
      <category>sentry</category>
      <category>logging</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Setting Up React Native Jest Tests With Higher Order Components</title>
      <dc:creator>Bionic Julia</dc:creator>
      <pubDate>Sat, 12 Feb 2022 00:00:00 +0000</pubDate>
      <link>https://forem.com/bionicjulia/setting-up-react-native-jest-tests-with-higher-order-components-mn4</link>
      <guid>https://forem.com/bionicjulia/setting-up-react-native-jest-tests-with-higher-order-components-mn4</guid>
      <description>&lt;p&gt;If your React Native app is anything like our app, you’ve got wrappers upon wrappers, &lt;em&gt;wrapping&lt;/em&gt; your screens and screen components. Some examples might be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.expo.dev/versions/latest/sdk/safe-area-context/"&gt;SafeAreaProvider&lt;/a&gt; - to ensure you’re only accessing a device’s safe area&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://styled-components.com/docs/advanced"&gt;ThemeProvider&lt;/a&gt; - say you were using something like Styled Components to provide a theme context to your entire app&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://redux-toolkit.js.org/"&gt;Redux&lt;/a&gt; - to manage state across your app&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This can make things tricky when it comes to writing your unit and integration tests, as your components might inadvertently depend on something that’s being provided by one or more of your higher order components (HoCs).&lt;/p&gt;

&lt;p&gt;In an attempt to simplify the setup of our Jest tests, we wrote some helper functions to make it easier to tap into the HoCs we needed on a test by test basis. Making things simpler means lowering the barrier to writing more tests whilst shortening development time, so this is a major win. 🎉&lt;/p&gt;

&lt;p&gt;Here’s an example of how it can be done in Typescript. The external packages we use are Redux Toolkit, Styled Components and React Native Safe Area Context.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// testHelpers.tsx&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getDefaultMiddleware&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@reduxjs/toolkit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;lodash&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lodash&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;SafeAreaProvider&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-native-safe-area-context&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Provider&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;ReduxProvider&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-redux&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;renderer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ReactTestInstance&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-test-renderer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;createMockStore&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;redux-mock-store&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ThemeProvider&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;styled-components/native&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;TRootState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@app/core/state/root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;initialState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@app/core/state/mockedInitialState&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@app/themes&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;DeepPartial&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;P&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;]?:&lt;/span&gt; &lt;span class="nx"&gt;DeepPartial&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;P&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;TConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;mockRedux&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;
  &lt;span class="nx"&gt;mockSafeAreaProvider&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;
  &lt;span class="nx"&gt;mockTheme&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;
  &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;DeepPartial&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TRootState&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;initialMetrics&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;insets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;createMockedElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ReactElement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;TConfig&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;mockedElement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;mockRedux&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;middlewares&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;getDefaultMiddleware&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mockStore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createMockStore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;middlewares&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;lodash&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;initialState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mockStore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;mockedElement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ReduxProvider&lt;/span&gt; &lt;span class="na"&gt;store&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;mockedElement&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;ReduxProvider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;mockTheme&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;mockedElement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ThemeProvider&lt;/span&gt; &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;mockedElement&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;ThemeProvider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;mockSafeAreaProvider&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;mockedElement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;SafeAreaProvider&lt;/span&gt; &lt;span class="na"&gt;initialMetrics&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;initialMetrics&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;mockedElement&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;SafeAreaProvider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;mockedElement&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;createReactTestInstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ReactElement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;TConfig&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;ReactTestInstance&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;renderer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;createMockedElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;root&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There’s quite a lot going on here, so let’s break it down. But first, we should talk about...&lt;/p&gt;

&lt;h3&gt;
  
  
  How the helper functions will be used in practice
&lt;/h3&gt;

&lt;p&gt;I always find it easier to first understand how we’d want to use these helper methods in the wild. I’ve therefore added an example of how we’d integrate these helpers into our tests. Note that this uses &lt;a href="https://reactjs.org/docs/test-renderer.html"&gt;React’s Test Renderer&lt;/a&gt; which is useful for say, checking for the presence of expected elements.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createReactTestInstance&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./testHelpers&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;MyComponent tests&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;renders correct version for users who shown interest&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;instance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createReactTestInstance&lt;/span&gt;&lt;span class="p"&gt;(&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;MyComponent&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;)&lt;/span&gt;

    &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;findByProps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;testID&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`interested-icon`&lt;/span&gt; &lt;span class="p"&gt;})).&lt;/span&gt;&lt;span class="nx"&gt;toBeTruthy&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;renders correct version for users who have not shown interest&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;instance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createReactTestInstance&lt;/span&gt;&lt;span class="p"&gt;(&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;MyComponent&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;)&lt;/span&gt;

    &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;findByProps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;testID&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`not-interested-icon`&lt;/span&gt; &lt;span class="p"&gt;})).&lt;/span&gt;&lt;span class="nx"&gt;toBeTruthy&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you wanted to test for whether certain user actions result in specific expectations, the &lt;a href="https://testing-library.com/docs/react-native-testing-library/intro"&gt;React Testing Library&lt;/a&gt; (which sits on top of React’s Test Renderer) is great for that. Rather than using our &lt;code&gt;createReactTestInstance&lt;/code&gt; helper, we can just tap into the &lt;code&gt;createMockedElement&lt;/code&gt; helper. Here’s an example.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;fireEvent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;render&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@testing-library/react-native&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;act&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-test-renderer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createMockedElement&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./testHelpers&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;navigateMock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jest&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mock&lt;/span&gt;
  &lt;span class="c1"&gt;// your mock...&lt;/span&gt;
  &lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;BackButton tests&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;navigates to the right screen onPress&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mockedElement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createMockedElement&lt;/span&gt;&lt;span class="p"&gt;(&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;BackButton&lt;/span&gt; &lt;span class="na"&gt;previousScreen&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"PreviousScreenName"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;)&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;renderAPI&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mockedElement&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;act&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;backButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;renderAPI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getByTestId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;button-back-navigation&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fireEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;press&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;backButton&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;navigateMock&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toHaveBeenCalledWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;PreviousScreenName&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that you understand how the helper functions are going to be used in practice, let’s go back to how we set up the helpers file.&lt;/p&gt;

&lt;h3&gt;
  
  
  Breaking how the helpers file
&lt;/h3&gt;

&lt;p&gt;At the heart of this file is the &lt;code&gt;createMockedElement&lt;/code&gt; function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;createMockedElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ReactElement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;TConfig&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;mockedElement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;mockRedux&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;middlewares&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;getDefaultMiddleware&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mockStore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createMockStore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;middlewares&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;lodash&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;initialState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mockStore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;mockedElement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ReduxProvider&lt;/span&gt; &lt;span class="na"&gt;store&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;mockedElement&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;ReduxProvider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;mockTheme&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;mockedElement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ThemeProvider&lt;/span&gt; &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;mockedElement&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;ThemeProvider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;mockSafeAreaProvider&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;mockedElement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;SafeAreaProvider&lt;/span&gt; &lt;span class="na"&gt;initialMetrics&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;initialMetrics&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;mockedElement&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;SafeAreaProvider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;mockedElement&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This function takes two arguments - the element/component you want to test, and an optional &lt;code&gt;config&lt;/code&gt; object. This config object allows you to specify what wrappers to include when rendering your component during the test (if any). For example, if you need to mock the Redux state, you can set up your test in this way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;doesn't open the modal when row is active&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mockedState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;show_modal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;mockedState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mockedElement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createMockedElement&lt;/span&gt;&lt;span class="p"&gt;(&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Row&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;,&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;renderAPI&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mockedElement&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;act&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ... your test expectations&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can similarly do the same if you need to include the &lt;code&gt;ThemeProvider&lt;/code&gt; and / or &lt;code&gt;SafeAreaProvider&lt;/code&gt; wrappers. As defined in &lt;code&gt;TConfig&lt;/code&gt;, note that these two options take &lt;code&gt;boolean&lt;/code&gt; inputs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Deeper dive into setting up Redux state
&lt;/h3&gt;

&lt;p&gt;When mocking the Redux state, you’ll likely need to ensure your test Redux state has been set up with some initial values. To do this, we extracted all the initial states out from our various Redux Toolkit slices and combined it into a single object, which we then passed into the &lt;code&gt;lodash&lt;/code&gt; merge function (to ensure it deep merges with our mocked state).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// @app/core/state/mockedInitialState&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;initialStateFeature1&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@covid/core/state/feature1.slice&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;initialStateFeature2&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@covid/core/state/feature2.slice&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;initialStateFeature3&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@covid/core/state/feature3.slice&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;initialState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TRootState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;feature1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;initialStateFeature1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;feature2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;initialStateFeature2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;feature3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;initialStateFeature3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that’s it! Hopefully this makes your React Native testing life a little easier. 😄 If you’ve got any suggestions or improvements for me, do let me know - I’m always keen to up my testing game! I'm at &lt;a href="https://bionicjulia.com"&gt;https://bionicjulia.com&lt;/a&gt;, &lt;a href="https://twitter.com/bionicjulia"&gt;Twitter&lt;/a&gt; and &lt;a href="https://instagram.com/bionicjulia"&gt;Instagram&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>jest</category>
      <category>reactnative</category>
      <category>typescript</category>
      <category>react</category>
    </item>
    <item>
      <title>React Formik and Yup Troubleshooting Tips</title>
      <dc:creator>Bionic Julia</dc:creator>
      <pubDate>Sat, 29 Jan 2022 00:00:00 +0000</pubDate>
      <link>https://forem.com/bionicjulia/react-formik-and-yup-troubleshooting-tips-2ipm</link>
      <guid>https://forem.com/bionicjulia/react-formik-and-yup-troubleshooting-tips-2ipm</guid>
      <description>&lt;p&gt;I’ve been spending the last couple of weeks reworking how we render forms in our React Native app. The main driver for this is a new feature we’re releasing soon, that requires over 150 questions to be asked to the user. In order to minimise code duplication and to make the editing of form questions easier going forward, we decided to go down the route of programmatically rendering the various forms and their questions from a large JSON file.&lt;/p&gt;

&lt;p&gt;Rendering our forms this way also meant having to &lt;a href="https://dev.to/blog/generating-yup-validation-object-with-map-function"&gt;programmatically generate the required Yup validation object&lt;/a&gt;. As we’re going to be migrating users across from an older system, there’s also an element of certain forms having pre-filled existing data that needs validating.&lt;/p&gt;

&lt;p&gt;So far, it all seems to be working out great, but there were some issues I can across during the build that I thought I’d note down. This is mostly for my future self as I’m sure I had come across some of these same issues in the past but had forgotten about them...so here’s to saving my future self some time. 😃&lt;/p&gt;

&lt;h3&gt;
  
  
  Issue: If your form is pre-populated with data and validation doesn’t work on component mount
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Ensure you’ve set &lt;code&gt;validateOnMount={true}&lt;/code&gt; within your &lt;code&gt;Formik&lt;/code&gt; component props. Also, check that you &lt;strong&gt;haven’t&lt;/strong&gt; included &lt;code&gt;formikProps.dirty&lt;/code&gt; as an additional check to enable your submit button (which I may or may not have done, then wasted 30 minutes trying to figure out why my button continued to remain disabled). 😅
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Formik&lt;/span&gt;
  &lt;span class="na"&gt;validateOnMount&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;initialValues&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;initialValues&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;onSubmit&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;onSubmit&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;validationSchema&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;validationSchema&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="err"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
 &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;formikProps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Form&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Issue: Not all field validations are working as expected
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Check that you’ve initialised each field value to at least an empty string, otherwise Yup will not know that the field(s) exists and will not be able to validate it.&lt;/li&gt;
&lt;li&gt;For context, I had some conditional fields that were only required if certain criteria were met. These fields were being ignored, because they were not initialised when the form component was mounted.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Issue: How to access &lt;code&gt;formikProps&lt;/code&gt; outside the form component
&lt;/h3&gt;

&lt;p&gt;There are a few ways of doing this depending on what specifically you’re trying to achieve.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you need to access &lt;code&gt;formikProps&lt;/code&gt; within a child component of the form, you can either pass it down as a prop, or use the &lt;a href="https://formik.org/docs/api/useFormikContext"&gt;&lt;code&gt;useFormikContext()&lt;/code&gt; hook&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;If you need to access &lt;code&gt;formikProps&lt;/code&gt; at the form component level, but outside the render method, create a React &lt;code&gt;ref&lt;/code&gt; and use the &lt;code&gt;innerRef&lt;/code&gt; Formik property to reference your form (and access the Formik methods).
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MyForm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;formRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleSubmit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;formRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;formRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handleSubmit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Formik&lt;/span&gt;
      &lt;span class="na"&gt;validateOnMount&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="na"&gt;innerRef&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;formRef&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="na"&gt;initialValues&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;initialValues&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="na"&gt;onSubmit&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;onSubmit&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="na"&gt;validationSchema&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;validationSchema&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="err"&gt;...&lt;/span&gt;
    &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    // ...
  )
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Issue: Using React lifecycle hooks that are dependent on &lt;code&gt;formikProps&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;For context, the issue I was faced with was how to check when a user had filled in specific form fields and moved on to the next question (for animation purposes). A solution I came up with was to utilise the &lt;code&gt;useEffect&lt;/code&gt; hook to check if my requirement passed, whenever the &lt;code&gt;formikProps.values&lt;/code&gt; array had been updated.&lt;/p&gt;

&lt;p&gt;I was using ES6 arrow function syntax, and was getting an error when I tried to do this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Formik&lt;/span&gt;
  &lt;span class="err"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;formikProps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;updateQuestionsState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;formikProps&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Form&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;

    &lt;span class="err"&gt;)&lt;/span&gt;
  &lt;span class="err"&gt;}}&lt;/span&gt;
&lt;span class="err"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="na"&gt;Formik&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To fix the error, I subbed out the arrow function with a named function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Formik&lt;/span&gt;
  &lt;span class="err"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;FormWithUseEffect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;formikProps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;updateQuestionsState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;formikProps&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Form&lt;/span&gt; &lt;span class="err"&gt;...&lt;/span&gt;

    &lt;span class="err"&gt;)&lt;/span&gt;
  &lt;span class="err"&gt;}}&lt;/span&gt;
&lt;span class="err"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="na"&gt;Formik&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you have any suggestions on how I might improve this post, please do also let me know. I'm at &lt;a href="https://bionicjulia.com"&gt;https://bionicjulia.com&lt;/a&gt;, &lt;a href="https://twitter.com/bionicjulia"&gt;Twitter&lt;/a&gt; and &lt;a href="https://instagram.com/bionicjulia"&gt;Instagram&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>forms</category>
      <category>formik</category>
      <category>yup</category>
      <category>react</category>
    </item>
    <item>
      <title>A Guide For New Programmers (Part 3)</title>
      <dc:creator>Bionic Julia</dc:creator>
      <pubDate>Sat, 22 Jan 2022 00:00:00 +0000</pubDate>
      <link>https://forem.com/bionicjulia/a-guide-for-new-programmers-part-3-5f8e</link>
      <guid>https://forem.com/bionicjulia/a-guide-for-new-programmers-part-3-5f8e</guid>
      <description>&lt;p&gt;This is the final part in a 3 part series, so if you haven’t already done so, be sure to first check out &lt;a href="https://dev.to/blog/guide-for-new-programmers-part-1"&gt;how I got started with programming (part 1)&lt;/a&gt; and &lt;a href="https://dev.to/blog/guide-for-new-programmers-part-2"&gt;how I learn new things (part 2)&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The aim of this blog post is to provide some guidance on questions to ask yourself if you’re starting from scratch. This is the #1 topic most people tend to send me questions on, but is one that I also cannot provide a succinct response to, because it’ll differ for everyone. This post is my attempt to provide an answer in a more helpful way.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I’d do if I was starting out today
&lt;/h2&gt;

&lt;p&gt;If I were to start again from scratch today, here are the list of questions I’d be asking myself to get a sense of the direction I need to take. In order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Why do I want to learn programming?&lt;/li&gt;
&lt;li&gt;What goal do I want to accomplish as a first step?&lt;/li&gt;
&lt;li&gt;What areas of tech / programming am I most interested in?&lt;/li&gt;
&lt;li&gt;What are the skills within this area that will best enable me to get to where I want to go?&lt;/li&gt;
&lt;li&gt;How should I prioritise the learning of these skills?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once you’ve answered question 5, you should have a list things you want to learn and what to learn first. This is where &lt;a href="https://dev.to/blog/guide-for-new-programmers-part-2"&gt;part 2 of this blog post series&lt;/a&gt; kicks in. 😀 As the questions above might seem a little abstract, I’ll walk through each one giving some examples along the way to help illustrate my thoughts.&lt;/p&gt;

&lt;h3&gt;
  
  
  Question 1: Why do I want to learn programming?
&lt;/h3&gt;

&lt;p&gt;The world of tech and programming is absolutely massive, with an infinite number of things you could learn. Being able to articulate why you want to learn programming will go a long way to giving you direction and helping you filter out a lot of what you &lt;em&gt;think&lt;/em&gt; you might want to learn.&lt;/p&gt;

&lt;p&gt;The other thing to note is that learning programming is a long road, and it can be difficult and frustrating at times. You’ll need to be motivated by something to ensure you keep going. &lt;strong&gt;Knowing why you really want to do this can be a good driving force for when times get tough.&lt;/strong&gt; The deeper you’ve thought about the why, the stronger its hold will be on you - this is something you really want to connect to on a deep, emotional level.&lt;/p&gt;

&lt;p&gt;Some examples might be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I want to make a career change from finance to tech because....[fill in the blank].&lt;/li&gt;
&lt;li&gt;I want a job that allows me to work remotely and with flexible hours, so I can spend more time with my family.&lt;/li&gt;
&lt;li&gt;I would like to increase my efficiency at work so I can spend my time learning new skills that will enable me to get promoted and increase my impact at work.&lt;/li&gt;
&lt;li&gt;I would like to publish a game on Steam and earn recurring revenue, so I don’t have to continue working 2 jobs.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Question 2: What goal do I want to get to as a first step?
&lt;/h3&gt;

&lt;p&gt;Once you know your “why” (i.e. the direction you want to go), the next thing to determine is where you want to get to as a first concrete step (your first milestone on your journey). If your why was, “I want to make a career change from finance to wellness-tech”, your goal might be, “To get a job at Peloton”.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The main reason I think it’s important to clarify what your first goal is, is so that you can learn with a concrete purpose in mind.&lt;/strong&gt; It will help you focus specifically on solving the problem at hand, rather than learning for learning’s sake.&lt;/p&gt;

&lt;p&gt;Tips:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;To make your goal stronger, add a timeframe and make it as specific as you can e.g. “To get a job at Peloton within the next 12 months, working on technology a user interfaces with on a daily basis”.&lt;/li&gt;
&lt;li&gt;To make your goal more achievable, set yourself up for success and think about what the smallest possible realistic representation of that milestone might be. i.e. don’t try to do too many things at once. Make it small, concrete and measurable so that you know when you’ve achieved that goal.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Other examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why: Increasing efficiency at work. Goal: Learn how to automate the transfer of data from CSV to company database, freeing up 4 hours in my working week.&lt;/li&gt;
&lt;li&gt;Why: Publish a game on Steam. Goal: Get a minimum viable game working on my computer by Christmas, with a single level, allowing players to collect coins.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Question 3: What areas of tech / programming am I most interested in?
&lt;/h3&gt;

&lt;p&gt;The answer to this question might already be obvious to you from the answers you came up with in questions 1 and 2, but if not, I’d really encourage you to have a think about what areas of tech and programming really excites you.&lt;/p&gt;

&lt;p&gt;Some of the main areas you might want to consider here are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Backend development - working with databases, servers and designing APIs.&lt;/li&gt;
&lt;li&gt;Frontend development - working with user interfaces and experiences.&lt;/li&gt;
&lt;li&gt;Systems engineering and reliability - overseeing the entire system and ensuring all parts are working seamlessly together.&lt;/li&gt;
&lt;li&gt;Games development - designing and developing games.&lt;/li&gt;
&lt;li&gt;Hardware development - working on the interfaces between electronics and mechanical system elements.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Whilst you can, of course, decide you want to go after more than one area, like full-stack development (both backend and frontend), &lt;strong&gt;I’d really encourage you to choose one area to lead with as a first step. The main reason for this is focus, especially in the early days when you’re learning everything from scratch and there is &lt;em&gt;a lot&lt;/em&gt; to learn.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Side note: If you are interested in full-stack development, my humble opinion is to first start learning backend development and understanding data transfer between databases through to APIs to be consumed by the frontend. It feels easier to then continue with learning frontend development. I’m possibly biased because that was the path I took. 😝&lt;/p&gt;

&lt;p&gt;Once you’ve identified the area you want to start with, revisit question 2 to see if there’s anything you’d change in your answer to make it more precise / clearer. &lt;strong&gt;It’s an iterative process, with the goal being to determine the smallest next step you can take, that still remains a meaningful goal to you.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Question 4: What are the skills within this area that will best enable me to get to where I want to go?
&lt;/h3&gt;

&lt;p&gt;The answer to this question will really depend on what you answered for question 2. It will also be clearer, the more precise and detailed your answer to question 2 is. If you’re having trouble answering this question, go back to question 2 and have a deeper think about how to define a smaller goal. 🙃&lt;/p&gt;

&lt;p&gt;Some examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Goal: get a development role at Peloton. Area: frontend development.

&lt;ul&gt;
&lt;li&gt;At this point, do some research to find out what Peloton’s tech stack is. You might find out they use React + NextJS on their website, and React Native for their mobile app. This might be sufficient for you, but you might also want to go back and revisit your goal to be more focused.&lt;/li&gt;
&lt;li&gt;Revised goal: get a frontend development role at Peloton, working on their mobile app. Area: frontend mobile development. Skills: React Native.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Goal: learn how to automate the transfer of data from CSV to company database. Area: backend development.

&lt;ul&gt;
&lt;li&gt;Let’s say your company uses Postgres for its database. One of the necessary skills would be SQL. You’d then have to do some research to figure out how you might be able to transfer CSV data to SQL commands. From your research, you might learn that you can use Python or Ruby. You then need to make a choice at this point, perhaps taking considerations like what backend languages your company uses, or what the most popular language used in industry is today, into account.&lt;/li&gt;
&lt;li&gt;From here, you might say the skills you need are Python and SQL.&lt;/li&gt;
&lt;/ul&gt;


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

&lt;h3&gt;
  
  
  Question 5: How should I prioritise the learning of these skills?
&lt;/h3&gt;

&lt;p&gt;If you’ve managed to nail it down to one skill - well done! Check out my &lt;a href="https://dev.to/blog/guide-for-new-programmers-part-2"&gt;second post&lt;/a&gt; in this series if you need some guidance on how to learn something new. If you’ve landed on more than one skill to learn, you can either: (i) try to be more precise in your goal / area and therefore skills needed, or (ii) just roll with it, because honestly, it might just be necessary.&lt;/p&gt;

&lt;p&gt;If this is the case, my suggestion would be to prioritise the skill you find most interesting or easiest in the first instance. What you want in the early days are quick wins, to prove to yourself that you can do this and that you’re progressing in tangible ways. This will give you the motivation to continue learning and pushing the boundaries of your knowledge.&lt;/p&gt;

&lt;p&gt;As you make progress in the first skill, you might then choose to commence learning the second skill in parallel. I’m a strong proponent of learning with the aim of solving your problem, so going back to the example above, the aim here isn’t to learn everything about Python, and then everything about SQL - it’s about learning enough Python to understand how to manipulate CSV data, then learning enough SQL to save that data to a Postgres database.&lt;/p&gt;

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

&lt;p&gt;As I’ve mentioned in my previous posts, this is just my thought process and opinion on how to go about learning programming. If this helped you in anyway, I’d love to hear about it. If you have any suggestions on how I might improve this post, please do also let me know. I'm at &lt;a href="https://bionicjulia.com"&gt;https://bionicjulia.com&lt;/a&gt;, &lt;a href="https://twitter.com/bionicjulia"&gt;Twitter&lt;/a&gt; and &lt;a href="https://instagram.com/bionicjulia"&gt;Instagram&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And finally, good luck! You’ve got this! 💪🏻😃&lt;/p&gt;

</description>
      <category>career</category>
    </item>
    <item>
      <title>A Guide For New Programmers (Part 2)</title>
      <dc:creator>Bionic Julia</dc:creator>
      <pubDate>Sat, 15 Jan 2022 00:00:00 +0000</pubDate>
      <link>https://forem.com/bionicjulia/a-guide-for-new-programmers-part-2-3l30</link>
      <guid>https://forem.com/bionicjulia/a-guide-for-new-programmers-part-2-3l30</guid>
      <description>&lt;p&gt;First things first, if you haven’t already done so, be sure to read my &lt;a href="https://dev.to/blog/guide-for-new-programmers-part-1"&gt;first post in this series&lt;/a&gt; for a little more context.&lt;/p&gt;

&lt;p&gt;Otherwise, jumping straight in where I left off...&lt;/p&gt;

&lt;h2&gt;
  
  
  How I learn new things and continue to develop my programming skills
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Back to the bootcamp
&lt;/h3&gt;

&lt;p&gt;One of the first things that was drummed into us from Day 1 of the coding bootcamp was that it’s not really the language or framework you’re learning that’s important. It’s much more about meta-learning i.e. learning about learning, and more specifically, learning how you learn.&lt;/p&gt;

&lt;p&gt;I can’t say it clicked immediately. When I first started, I was learning all sorts of new things I’d never come across before - command line, git, Ruby, Ruby on Rails, APIs, for loops, switch statements, MVCs etc. and it just seemed like there was an overwhelming number of things to learn, for say just Ruby alone! Forget about learning how to learn, I just needed to buckle down and learn about Ruby!&lt;/p&gt;

&lt;p&gt;And whilst yes, there is an element of having to do that in the early days, I soon reached a point where I realised:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;I was never going to be able to memorise all of these things;&lt;/li&gt;
&lt;li&gt;Programming languages and frameworks follow certain patterns; and&lt;/li&gt;
&lt;li&gt;There’s this thing called Google and the Internet which helps you find the answers and things you might need.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Why meta-learning is so useful
&lt;/h3&gt;

&lt;p&gt;To give you a concrete example, I started by learning Ruby as my introductory programming language. You learn about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Types like strings, integers and booleans;&lt;/li&gt;
&lt;li&gt;Data structures like arrays and objects; and&lt;/li&gt;
&lt;li&gt;Data flow using if statements and loops (amongst, of course, a whole host of other things).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I spent a lot of the early days perusing the Ruby documentation, learning about how you manipulate data with the various methods and notation. This was 100% time well-spent as it taught me a lot about the lower level workings of the language, which is useful as a beginner. However, in the years since, I’ve stopped programming in Ruby and now use Python and JavaScript as my day-to-day programming languages... so what’s one to do? Read through all the Python docs and JavaScript docs? Take another bootcamp for those languages?&lt;/p&gt;

&lt;p&gt;Well maybe yes, if your role requires you to learn a particular language for its very specific features, but I’d guess that for most people this isn’t the case. When I first had to code in JavaScript and Python, guess what? They’ve both got concepts of types, data structures and data flow as flow. When I sat down to work on my first Python repo then, rather than having to spend days reading Python docs and practicing methods, it was really more about recognising core programming and software development concepts, figuring out the initial questions to ask, and then using the internet to find relevant resources and iteratively improve on the phrasing of those questions to get me closer to the potential answers I needed.&lt;/p&gt;

&lt;p&gt;It’s similar with frameworks by the way. I started with Ruby on Rails, where I learnt about concepts like Model-View-Controller (MVC), relational databases (Postgres) &amp;amp; ORMs, and how routing works. Since working with Python, I’ve had to learn FastAPI and Django. And this was all for a new job I accepted, which meant I didn’t have the luxury of taking my own sweet time to learn them - I had to figure out how to get up to speed ASAP (and not least because I had a probation period to pass lol). 🙈&lt;/p&gt;

&lt;p&gt;People learn best in different ways, so once again, the only thing I can do here is communicate what I did (and continue to do), to learn quickly and level up. One quick aside before I start - you can of course, choose to stick with the same programming language / framework and gain expertise in one thing. Just always be aware that technology and how we develop software moves extremely quickly, with languages going in and out of fashion, and different problems requiring different languages to solve them. I’d also say that the meta-learning skill can be applied to other areas in your life, where you might be trying to learn something new. e.g. I’m teaching myself how to play the drums and it’s been brilliant.&lt;/p&gt;

&lt;h2&gt;
  
  
  Julia’s method
&lt;/h2&gt;

&lt;p&gt;Here are the steps I tend to go through whenever I want to learn something new. To help communicate what might be fairly abstract ideas, I’ll use two examples: (i) learning a new framework like React; and (ii) learning how to play the drums.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Step 1: Figure out the core concepts that underpin the foundations of what you’re trying to learn&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;This step is essentially about trying to &lt;strong&gt;get a list of the fundamental concepts that underpin what you’re trying to learn, as quickly as possible&lt;/strong&gt;. What’s the minimum set of ideas / core concepts you need to understand, to be able to deliver something that works? Filtering out the unnecessary allows you to focus on what will help you make the most progress in the shortest amount of time. &lt;strong&gt;Making progress quickly keeps you motivated to keep learning.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Resources I tend to use for this step

&lt;ul&gt;
&lt;li&gt;Short YouTube videos to get a quick demo / lay of the land.&lt;/li&gt;
&lt;li&gt;“Experts” who’ve been there and done that, like senior developers and industry influencers - ideally someone you know and trust.&lt;/li&gt;
&lt;li&gt;Paid coaching - can be expensive, but potentially saves a lot of time as you don’t then have to filter out bad information yourself.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Examples

&lt;ul&gt;
&lt;li&gt;React: understanding component lifecycles and props vs. state, so I can get a parent and child component rendering on screen with dynamic information. 👩‍👦&lt;/li&gt;
&lt;li&gt;Drumming: learning how to count beats and get basic limb independence for the snare drum, hi-hat and bass pedal, so I can play an 8-bar beat for a classic rock song. 🎶&lt;/li&gt;
&lt;/ul&gt;


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

&lt;h3&gt;
  
  
  Step 2: Practice those concepts in “real world” scenarios
&lt;/h3&gt;

&lt;p&gt;Once you’ve identified what you need to learn in the first instance, begin actively practicing them. You can start by reading about it or watching videos, but try to move on to actual “doing” as quickly as possible, as it forces you to think about &lt;strong&gt;how&lt;/strong&gt; to do something, which in turn &lt;strong&gt;aids with better retention&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;What I also try to do is practice in different environments, with each progression getting me closer to a “real world” application. Why? Because real world problems force you to think more realistically about what you’re trying to do, and &lt;strong&gt;what you need to learn next to get there&lt;/strong&gt;. i.e. it guides you to learn with context (which I’ve found to be so much more effective, vs. learning from set examples). Sandbox problems are great to start, but you’ll reach a plateau in your learning if you stay there.&lt;/p&gt;

&lt;p&gt;Playing around in different environments also gives you the chance to apply your knowledge in different contexts, which helps build your &lt;a href="https://jamesclear.com/mental-models"&gt;mental model&lt;/a&gt; of how something works. It’s also immensely satisfying to be building something that solves a real problem, as opposed to just working on something for the sake of it. 🙂&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Potential resources

&lt;ul&gt;
&lt;li&gt;If you’re learning a new programming language, HackerRank / CodeWars / LeetCode.&lt;/li&gt;
&lt;li&gt;Reading through your company’s code repo and working on tickets (can be intimidating, but it’s effective).&lt;/li&gt;
&lt;li&gt;Similarly, find relevant open source repos, read through the code, and contribute what you can.&lt;/li&gt;
&lt;li&gt;YouTube videos for finding ideas of what you can build and walking you through the steps (but do this actively, rather than just sitting back and watching).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Examples

&lt;ul&gt;
&lt;li&gt;React: the usual - build your own To-do app or a weather app. 😉&lt;/li&gt;
&lt;li&gt;Drumming: pick a song you want to play, and start jamming. 🤘🏻&lt;/li&gt;
&lt;/ul&gt;


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

&lt;h3&gt;
  
  
  Step 3: Structured learning
&lt;/h3&gt;

&lt;p&gt;If you’ve been learning in real-world environments, you’ll soon begin to realise how much there is that you still know nothing about. It’s really easy to start losing hope at this stage, but don’t! Take heart - this is a great sign of progress because it shows you’ve been pushing your boundaries and dealing with real world complexity, which is, of course, not going to be easy.&lt;/p&gt;

&lt;p&gt;The important thing to note here is how to structure your learning so you &lt;strong&gt;don’t get bogged down in trying to pursue too many things or the wrong things&lt;/strong&gt;. This starts with developing the skill of asking the right questions, because once you know what those are, it’s generally pretty easy to find the answers by using Google or YouTube search. It will be an &lt;strong&gt;iterative process&lt;/strong&gt;, which gets easier over time, the more your mental model builds.&lt;/p&gt;

&lt;p&gt;What I’ll tend to do is the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Come across an issue to solve. Keep Googling until I find potential solutions that solves the one issue I’m trying to solve.&lt;/li&gt;
&lt;li&gt;Along the way, I’ll make a note of topics I come across, which I don’t know / understand, and that I want to do further reading on. However, I won’t spend a lot of time on those yet, as it’s easy to get bogged down, without even knowing how crucial it is to know those things at this stage.&lt;/li&gt;
&lt;li&gt;I’ll continue just Googling and learning only the things necessary to solve my immediate problem. Each new puzzle solved adds to my mental model of how I think [React / drumming / whatever] works. After a while, when I feel like there’s too much on my “to learn” list, or if I’m finding it hard to piece together new information to add to my mental model, I’ll look at finding a learning resource that deep dives on the topic, in an organised way.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;I’ll try to choosing a resource that’s active, rather than passive, because once again, it really helps things stick if you’re actively working through an issue. What I’ll generally find at this point is that I’ll be able to speed through materials, as there’ll be a lot that likely just serves to confirm (or correct) your mental model of how something works. At other points, you’ll hopefully get “a-ha” moments that allows groups of ideas you were struggling with to unlock and make sense.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Potential resources&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Paid learning platforms like &lt;a href="https://www.udemy.com/"&gt;Udemy&lt;/a&gt; and &lt;a href="http://skillshare.com"&gt;Skillshare&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Free learning platforms like &lt;a href="https://www.freecodecamp.org/"&gt;freeCodeCamp&lt;/a&gt; and &lt;a href="https://www.coursera.org/"&gt;Coursera&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Courses from industry experts from platforms like &lt;a href="https://frontendmasters.com/"&gt;Frontend Masters&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Books on a subject matter.&lt;/li&gt;
&lt;/ul&gt;


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

&lt;p&gt;Examples&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;React: Kent C. Dodds &lt;a href="https://epicreact.dev/"&gt;Epic React&lt;/a&gt; course ⚛️&lt;/li&gt;
&lt;li&gt;Drumming: Going through the &lt;a href="https://www.drumeo.com/"&gt;Drumeo&lt;/a&gt; Method course 🥢&lt;/li&gt;
&lt;/ul&gt;


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

&lt;h3&gt;
  
  
  Step 4: Continuous learning
&lt;/h3&gt;

&lt;p&gt;By this point, you should have gotten a good understanding for the new skill you’re trying to learn. You’ll also have realised there’s still a seemingly infinite number of things that you don’t know...so the learning doesn’t stop.&lt;/p&gt;

&lt;p&gt;This step should actually underpin all of the previous steps, as I’ve found that it helps keep my learning current and relevant, along with giving me new ideas and resources that helps further propel my learning. It’s all about consuming materials to diversify my sources and serves as unstructured learning, to fit around what I might be doing in the previous steps e.g. whilst I’m bored, going for a jog or commuting.&lt;/p&gt;

&lt;p&gt;Here’s an ordered list of the resources I tend to use (from most to least used)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Podcasts&lt;/li&gt;
&lt;li&gt;Books&lt;/li&gt;
&lt;li&gt;Newsletters&lt;/li&gt;
&lt;li&gt;Blog subscriptions&lt;/li&gt;
&lt;li&gt;YouTube channels&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;React: &lt;a href="https://changelog.com/jsparty"&gt;JS Party podcast&lt;/a&gt;, &lt;a href="https://opinionatedreact.com/"&gt;The Opinionated Guide To React book&lt;/a&gt;, &lt;a href="https://react.statuscode.com/"&gt;React Status newsletter&lt;/a&gt; 📕&lt;/li&gt;
&lt;li&gt;Drumming: &lt;a href="https://www.youtube.com/user/ggnoka82"&gt;Kristina Schiano YouTube&lt;/a&gt;, &lt;a href="https://drumeo.com"&gt;Drumeo&lt;/a&gt; 🥁&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Miscellaneous tips
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Take breaks when you’re not actively thinking about what you’re trying to learn and allow your brain to go into &lt;a href="https://www.brainscape.com/academy/focused-vs-diffuse-thinking-learning/#:~:text=Unlike%20focused%20thinking%2C%20diffuse%20thinking,brain%2C%20but%20rather%20all%20over."&gt;diffuse thinking mode&lt;/a&gt;. Ideally do something physically active, move your body and get a sweat on.&lt;/li&gt;
&lt;li&gt;Get adequate, good quality &lt;a href="https://healthysleep.med.harvard.edu/healthy/matters/benefits-of-sleep/learning-memory#:~:text=Healthy%20sleep%20is%20essential%20for%20optimal%20learning%20and%20memory%20function.&amp;amp;text=First%2C%20a%20sleep%2Ddeprived%20person,essential%20for%20learning%20new%20information."&gt;sleep&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.yourheights.com/blog/health/what-does-water-do-for-the-brain"&gt;Stay hydrated&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Phew, that was a long post! Once again, this is just my method, and what I’ve found works well for me. If you’ve got any suggestions, or have found specific methods that work for you, I’d love to hear from you. Also, if you have any feedback on how I might make my post clearer, give me a shout. I'm at &lt;a href="https://bionicjulia.com"&gt;https://bionicjulia.com&lt;/a&gt;, &lt;a href="https://twitter.com/bionicjulia"&gt;Twitter&lt;/a&gt; and &lt;a href="https://instagram.com/bionicjulia"&gt;Instagram&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>reflections</category>
      <category>career</category>
    </item>
    <item>
      <title>A Guide For New Programmers (Part 1)</title>
      <dc:creator>Bionic Julia</dc:creator>
      <pubDate>Sun, 09 Jan 2022 00:00:00 +0000</pubDate>
      <link>https://forem.com/bionicjulia/a-guide-for-new-programmers-part-1-3521</link>
      <guid>https://forem.com/bionicjulia/a-guide-for-new-programmers-part-1-3521</guid>
      <description>&lt;p&gt;One of the most frequently asked questions I get is where to start as a beginner wanting to learn programming. This question is so broad and so personal that I cannot really give an answer that will be true for everyone. What I can do though, is to give you my perspective and story on how I started out, what I do to continue learning and up-skilling, and finally, with the benefit of hindsight and experience, how I would think about making the decision of where to begin today.&lt;/p&gt;

&lt;p&gt;As there’s quite a lot to write about, I’ll break this topic down into a number of sections:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;How I got started with programming;&lt;/li&gt;
&lt;li&gt;How I learn new things; and finally&lt;/li&gt;
&lt;li&gt;What I’d do if I was starting out today.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  How I Got Started With Programming
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Back to the 90s
&lt;/h3&gt;

&lt;p&gt;It was back in the 90s, when we got our first family computer and dial-up internet at home, when I first started programming. It was pretty much just building personal websites using sites like Geocities, Blogger and LiveJournal, which is where I was first introduced to HTML and CSS. Things were relatively basic and simple then (I hadn’t come across Javascript), and I would spend hours making things like review sites and logging books read... but also connecting with other hobbyist tinkerers doing similar things and learning from them.&lt;/p&gt;

&lt;p&gt;The funny / sad thing to note, is that the vast majority of those online “friends” were female, so it would have been a realistic assumption to assume they’d graduate to become the developers of our current day... but it doesn’t seem to have been so. In my case, I never once even considered making software development a potential career - I only ever saw it as a hobby and nothing else. I didn’t know anyone around me who worked in tech, or as a developer, and it never crossed my mind that I could be one. Perhaps something similar happened with those other female hobbyist developers at that time as well, who knows? 🤷🏻‍♀️ I feel this goes to show just how important it is to get more minority representation out there, whatever the field, be it engineering, tech, finance etc.&lt;/p&gt;

&lt;p&gt;I ended up studying the sciences, then went on to do engineering and finance in university. After spending some years as an investment banker, I quit as I knew it wasn’t what I wanted to do with my life, long term. Finally giving myself the space to think about what it is that really gave me joy and fulfilment, I set my sights on two things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;I really wanted to work in the tech industry (it was pretty obvious that it was &lt;a href="https://future.a16z.com/software-is-eating-the-world/"&gt;eating the world&lt;/a&gt;, and I just love tech), but also...&lt;/li&gt;
&lt;li&gt;I knew I wanted to be a builder (after years of doing advisory in a bank). Knowing how much I used to love programming, my decision to develop these skills then became a really obvious one.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Why a coding bootcamp?
&lt;/h3&gt;

&lt;p&gt;Decision made, the next step was deciding how to actually develop the skills. My plan when I quit finance, was to go on a solo travel trip for 4-6 months. Before embarking on my trip, I signed up for a 5 day, intensive, front-end coding course (in-person coding courses / bootcamps were just starting to be a thing at that time). I learnt best practice HTML, CSS and basic JavaScript (with jQuery) and absolutely loved it.&lt;/p&gt;

&lt;p&gt;I went off travelling, had a blast, and didn’t think too much about career choices or anything like that, for months. When I got to the point of starting to tire of living nomadically, at around the 4-5 month mark, and thoughts of “what next?” entered my mind, I knew that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;I wasn’t going back to finance; and&lt;/li&gt;
&lt;li&gt;I wanted to give myself a few months to learn programming full-time, to see if this was really something I could realistically pursue.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That’s when I applied to a 3 month, full-time, in-person bootcamp in London... and it was pretty much one of the best decisions of my life. The thing to highlight though, is that bootcamps are &lt;strong&gt;definitely not for everyone&lt;/strong&gt;. They’re also not cheap, so if you’re trying to make this decision, here are some bullets on why it was a good decision &lt;strong&gt;for me&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I’m an all-in or nothing kind of person (and &lt;strong&gt;bootcamps are definitely an all-in commitment&lt;/strong&gt;). I don’t like doing things in half-measures. If I’m interested in something, I tend to immerse myself completely in that world, and can very quickly become obsessed with it. It’s true of a lot of my ongoing interests, and is both a good and bad trait.

&lt;ul&gt;
&lt;li&gt;It’s good in that I feel like I progress quickly in the early days, which gives me forward momentum to continue learning (flywheel effect).&lt;/li&gt;
&lt;li&gt;It’s bad in that it’s all consuming in my life, and pretty much everything else takes a backseat for some time.
What happens is that the fervour does eventually taper off, and I either drop that subject (e.g. investment banking), or it remains a core interest of mine, and I carry on progressing with it in more of a steady-state fashion (e.g. nutrition, health &amp;amp; fitness and drumming).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;One of the main skills the bootcamp focused on teaching us was learning how to learn. The languages and frameworks taught at the bootcamp ultimately only serves as an introduction to the basics of programming. In the real world, languages go in and out of fashion, and you’ll need to chose the best one for the problem at hand, rather than the one you know. &lt;strong&gt;It’s therefore much more important to learn how to learn new things quickly.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;I joined the bootcamp way before COVID was a thing. Learning in-person with other similarly minded folks really added to the immersive experience, as I was surrounded by others all wanting to learn quickly, and progress to the same goal. I was living, eating, sleeping and breathing code for, on average, 18 hours a days, for 3 months straight (and loving it!). It’s a lot like learning a spoken language - if you want to learn French, one of the fastest ways to do so is to go live in France and immerse yourself with other French-speaking people, 24/7.&lt;/li&gt;
&lt;li&gt;Bootcamps are &lt;strong&gt;really focused on giving you practical knowledge&lt;/strong&gt;. Keeping up with the syllabus meant I’d end each week with a working app. This was great from a GitHub portfolio building point of view, but also for building my confidence - it’s amazing to be able to mark your progress by interacting with programs you built by hand...and really satisfying. 😊

&lt;ul&gt;
&lt;li&gt;Note that bootcamps are generally hyper-focused on ensuring you develop practical skills because a lot of them will also have a job referral function to help place you in a role after you graduate. This is great, if getting a software developer job is what you want. One downside is that focusing so heavily on the practical means a lack of focus on theoretical computer science knowledge that makes one a well-rounded engineer. The good news is that you can learn this in your own time, after you graduate.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Part of the syllabus involved teaching us &lt;strong&gt;programming best practices&lt;/strong&gt;. This included usage of git and GitHub, Test Driven Development (TDD) and pair programming. You start by building good habits straight away.&lt;/li&gt;
&lt;li&gt;The final 2 weeks of the bootcamp were reserved for &lt;strong&gt;final projects&lt;/strong&gt;. It involved self-organising into groups of your choice, where you’d then decide on an app to build, build it, and finally present it to the entire bootcamp, alumni and businesses looking to recruit. It’s a practice mini-sprint with a deliverable at the end of it.&lt;/li&gt;
&lt;li&gt;Bootcamps are an interesting place in the types of people it attracts. My cohort included &lt;strong&gt;people from really different experiences and backgrounds&lt;/strong&gt; who I would typically never mix with. We were however, all united by the same goal...and having paid a lot of money to try to get there, were all super motivated to succeed. I was really lucky, and ended up meeting 2 people in the bootcamp who would become my co-founders upon graduating.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What the bootcamp isn’t
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;It isn’t personalised and the syllabus waits for no one. It’s solely up to you to keep yourself motivated and keep up with the fast pace of learning required.&lt;/li&gt;
&lt;li&gt;Though bootcamps are catered for pure beginners, because there’s so much to learn, it’s unlikely that they’ll take you through HTML and CSS.&lt;/li&gt;
&lt;li&gt;There was no handholding with the bootcamp I went to. In fact, it was the opposite where I was just dropped into the deep end. There was no homework. There was no exam. There were no marks. You put in as much or as little effort as you want. It was frustrating and it was difficult - you need to know whether you thrive or flounder in these types of situations.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Upon bootcamp graduation
&lt;/h3&gt;

&lt;p&gt;Though it isn’t recommended, I started networking and applying for jobs before graduating (the reason it’s not recommended is because they want you to focus on learning how to program, rather than worrying about finding a job). As a result of this, I actually received a couple of job offers immediately upon graduating (so hey, bootcamp works! 🙌🏻).&lt;/p&gt;

&lt;p&gt;I ultimately ended up going a completely different direction and started my own tech startup with a couple of friends I met at the bootcamp. Using our newfound coding skills, we created our MVP in a week, shipped it out for feedback, and went on our way! The main point to make here is to continue programming immediately upon graduation, and build real projects. This forward momentum is really important for habit building, to ensure you keep flexing your programming skills. Again, much like learning a language, you need to keep practicing until it’s ingrained, to ensure you don’t forget what you’ve learnt. 💪🏼&lt;/p&gt;

&lt;p&gt;I was also lucky to effectively form my own group of developers to work and engage with. The process of continuous learning is easier if you have a supportive group to help you along...because the learning really starts at this point. Having to program to solve real-life problems for the first time really drove home the point that there were (still are!) so many things that I didn’t even know I didn’t know. It’s all good though, because the meta skill you get of learning how to learn comes in handy at this point. 😉&lt;/p&gt;

&lt;p&gt;Next up: How I learn new things and continue to develop my programming skills.&lt;/p&gt;

&lt;p&gt;P.S. Talk to me on &lt;a href="https://instagram.com/bionicjulia"&gt;Instagram&lt;/a&gt;, &lt;a href="https://bionicjulia.com/twitter"&gt;Twitter&lt;/a&gt; or &lt;a href="https://bionicjulia.com"&gt;https://bionicjulia.com&lt;/a&gt;!&lt;/p&gt;

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