<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>React on blog.iankulin.com</title><link>https://blog.iankulin.com/tags/react/</link><description>Recent content in React on blog.iankulin.com</description><generator>Hugo</generator><language>en-AU</language><lastBuildDate>Fri, 26 Jan 2024 00:00:00 +0000</lastBuildDate><atom:link href="https://blog.iankulin.com/tags/react/index.xml" rel="self" type="application/rss+xml"/><item><title>Getting Your Vite React App to Work on Github Pages</title><link>https://blog.iankulin.com/getting-your-vite-react-app-to-work-on-github-pages/</link><pubDate>Fri, 26 Jan 2024 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/getting-your-vite-react-app-to-work-on-github-pages/</guid><description>&lt;img src="https://blog.iankulin.com/images/combined.png" width="512" alt=""&gt;
&lt;p&gt;One of the many cool things about GitHub is &lt;a href="https://pages.github.com"&gt;GitHub Pages&lt;/a&gt; - the free web hosting Microsoft gives you while they vacuum up &lt;a href="https://docs.github.com/en/copilot/overview-of-github-copilot/about-github-copilot-individual"&gt;your code for CoPilot&lt;/a&gt; training. Each repository you keep there can have pages at &lt;code&gt;&amp;lt;your-github-username&amp;gt;.github.io/&amp;lt;repo-name&amp;gt;&lt;/code&gt;&lt;/p&gt;
&lt;h3 id="github"&gt;GitHub&lt;/h3&gt;
&lt;p&gt;To enable this, you need to go into the settings for the repository - look down the left for &amp;ldquo;Pages&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2023-12-31-at-1.58.05-pm.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s possible to have it based on a complicated GitHub action (where your build step happens on GitHub when you push your code), but the easiest thing is just to have it deployed from a branch. To do this you choose which branch (usually main) and whereabouts in the main branch your HTML is. The choices are in the root of your project, or in the &lt;code&gt;/docs&lt;/code&gt; directory. I&amp;rsquo;ve chosen the &lt;code&gt;/docs&lt;/code&gt; directory in the screenshot above, since my messy React project is in the root.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s all the GitHub set up we need. Now whenever I push my project to the &lt;code&gt;main&lt;/code&gt; branch on GitHub, whatever is in the &lt;code&gt;/docs&lt;/code&gt; directory will be uploaded to my GitHub page for this repo.&lt;/p&gt;
&lt;h3 id="vitereact"&gt;Vite/React&lt;/h3&gt;
&lt;p&gt;Now we need to make a couple of changes to our project to get this to work. The first is to tell Vite the &amp;ldquo;base directory&amp;rdquo; for the project which needs to be the repo name you&amp;rsquo;ve used on GutHub.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://blog.iankulin.com/images/screen-shot-2023-12-31-at-4.04.50-pm.png"&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2023-12-31-at-4.04.50-pm.png" width="900" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This is written into the &lt;code&gt;index.html&lt;/code&gt; that is built as part of this process. If it&amp;rsquo;s not there, then any browser accessing your &lt;code&gt;index.html&lt;/code&gt; on gh-pages won&amp;rsquo;t be able to find your JavaScript, and the user will be left looking at a blank white page instead of your amazing app.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://blog.iankulin.com/images/screen-shot-2023-12-31-at-4.11.06-pm.png"&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2023-12-31-at-4.11.06-pm.png" width="900" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;My process from this point, is to build the project with &lt;code&gt;npm run build&lt;/code&gt;. By default, this creates a &lt;code&gt;/dist&lt;/code&gt; directory in your project (which is already added to &lt;code&gt;.gitignore&lt;/code&gt;) and puts the project artifacts (the HTML, JavaScript, CSS and any images) into it. I then manually copy the artifacts over to the &lt;code&gt;/docs&lt;/code&gt; directory of the project and push it up to GitHub to be published - which takes two or three minutes.&lt;/p&gt;
&lt;p&gt;I like this manual step of copying the files over so that publishing is an intentful action on my part, and also, for solo projects I generally just work out of the main branch rather than on feature branches that then get PR&amp;rsquo;d into main. If you did want the process to be more CI/CD flavoured, you can just make another change the &lt;code&gt;vite.config.ts&lt;/code&gt; file to have your builds go straight to the &lt;code&gt;/docs&lt;/code&gt; folder.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;import &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; defineConfig &lt;span style="color:#eceff4"&gt;}&lt;/span&gt; from &lt;span style="color:#a3be8c"&gt;&amp;#39;vite&amp;#39;&lt;/span&gt;import react from &lt;span style="color:#a3be8c"&gt;&amp;#39;@vitejs/plugin-react&amp;#39;&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;//&lt;/span&gt; https&lt;span style="color:#eceff4"&gt;:&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;//&lt;/span&gt;vitejs&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;dev&lt;span style="color:#81a1c1"&gt;/&lt;/span&gt;config&lt;span style="color:#81a1c1"&gt;/&lt;/span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;export&lt;/span&gt; default defineConfig&lt;span style="color:#eceff4"&gt;({&lt;/span&gt; base&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#34;/mosh-expense/&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; plugins&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#eceff4"&gt;[&lt;/span&gt;react&lt;span style="color:#eceff4"&gt;()],&lt;/span&gt; build&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; outDir&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#39;docs&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; &lt;span style="color:#eceff4"&gt;}})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Once all that&amp;rsquo;s working, and you&amp;rsquo;ve pushed your changes and waited a minute or two, your project should be live to the world on &lt;code&gt;github.io&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://blog.iankulin.com/images/screen-shot-2023-12-31-at-4.45.26-pm.png"&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2023-12-31-at-4.45.26-pm.png" width="900" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If you want users browsing your repo to find the live version, it&amp;rsquo;s worth editing your repository about settings to point to it.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://blog.iankulin.com/images/screen-shot-2023-12-31-at-4.47.30-pm.png"&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2023-12-31-at-4.47.30-pm.png" width="900" alt=""&gt;&lt;/a&gt;&lt;/p&gt;</description></item><item><title>React Expense Tracker App</title><link>https://blog.iankulin.com/react-expense-tracker-app/</link><pubDate>Mon, 22 Jan 2024 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/react-expense-tracker-app/</guid><description>&lt;p&gt;I&amp;rsquo;m focused on React frontend skills these holidays, and &lt;a href="https://codewithmosh.com/p/ultimate-react-part1"&gt;working through Mosh&amp;rsquo;s React 18&lt;/a&gt; course. The exercise today (which I think I nailed, although I spent more than the recommended hour on) was a small app to track expenses. Like most of Mosh&amp;rsquo;s exercises it was great because it exercised all the understandings up to that point - so it&amp;rsquo;s a good starting React project. It used Zod for the form validation which is completely new to me, but looks great.&lt;/p&gt;
&lt;h3 id="the-specification"&gt;The Specification&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://blog.iankulin.com/images/screen-shot-2023-12-31-at-6.02.08-am.png"&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2023-12-31-at-6.02.08-am.png" width="900" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This is an app for tracking expenses. Expenses can be in one of three &lt;em&gt;categories&lt;/em&gt;, they also must have a &lt;em&gt;description&lt;/em&gt; and an &lt;em&gt;amount&lt;/em&gt;. There&amp;rsquo;s a form for entering a new expense with a submit button. The form fields are validated before the expense item is added.&lt;/p&gt;
&lt;p&gt;Below the form for adding a new expense item is a list of expenses. Each one shows its description, amount and category. There is also a button to delete this expense. At the bottom of the list is a total for the expenses shown. The expenses shown in the list can be filtered by category with a drop down.&lt;/p&gt;
&lt;h3 id="design-decisions"&gt;Design Decisions&lt;/h3&gt;
&lt;p&gt;The first decision in an React app is &amp;ldquo;What are the components going to be?&amp;rdquo;. Clearly the form at the top is a component (I called mine &lt;code&gt;AddForm&lt;/code&gt;). The bottom section could be two - the filter and the list, but my style is to start with less components then seperate them out if they are getting complex so I considered the filtered list a single component called &lt;code&gt;ExpenseList&lt;/code&gt;. In Mosh&amp;rsquo;s solution, he did have these as separate components, arguing that the filter is likely to become more complex in future which is a sound argument, but too much premature optimisation for me.&lt;/p&gt;
&lt;h3 id="code"&gt;Code&lt;/h3&gt;
&lt;p&gt;With that decision made, we have enough to write the App.tsx. I also decided to persist the array of &lt;code&gt;Expense&lt;/code&gt;s to local storage to make it a nicer demo app. So with that, the app component code looks like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;import &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; useState&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; useEffect &lt;span style="color:#eceff4"&gt;}&lt;/span&gt; from &lt;span style="color:#a3be8c"&gt;&amp;#34;react&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;;&lt;/span&gt;import AddForm from &lt;span style="color:#a3be8c"&gt;&amp;#34;./AddForm&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;;&lt;/span&gt;import &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; Expense &lt;span style="color:#eceff4"&gt;}&lt;/span&gt; from &lt;span style="color:#a3be8c"&gt;&amp;#34;./types.ts&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;;&lt;/span&gt;import ExpenseList from &lt;span style="color:#a3be8c"&gt;&amp;#34;./ExpenseList&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;;&lt;/span&gt;import &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; v4 as uuidv4 &lt;span style="color:#eceff4"&gt;}&lt;/span&gt; from &lt;span style="color:#a3be8c"&gt;&amp;#34;uuid&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;;&lt;/span&gt;function App&lt;span style="color:#eceff4"&gt;()&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;const&lt;/span&gt; &lt;span style="color:#eceff4"&gt;[&lt;/span&gt;expenses&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; setExpenses&lt;span style="color:#eceff4"&gt;]&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; useState&lt;span style="color:#81a1c1"&gt;&amp;lt;&lt;/span&gt;Expense&lt;span style="color:#eceff4"&gt;[]&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;(()&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;//&lt;/span&gt; Try to &lt;span style="color:#81a1c1"&gt;load&lt;/span&gt; expenses from local storage &lt;span style="color:#81a1c1;font-weight:bold"&gt;const&lt;/span&gt; savedExpenses &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; localStorage&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;getItem&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;mosh-expense&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;);&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;if&lt;/span&gt; &lt;span style="color:#eceff4"&gt;(&lt;/span&gt;savedExpenses&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;return&lt;/span&gt; JSON&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;parse&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;savedExpenses&lt;span style="color:#eceff4"&gt;);&lt;/span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;else&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;//&lt;/span&gt; If there are no saved expenses&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;load&lt;/span&gt; the sample expenses &lt;span style="color:#81a1c1;font-weight:bold"&gt;return&lt;/span&gt; loadSampleExpenses&lt;span style="color:#eceff4"&gt;();&lt;/span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt; &lt;span style="color:#eceff4"&gt;});&lt;/span&gt; useEffect&lt;span style="color:#eceff4"&gt;(()&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;//&lt;/span&gt; Save expenses to local storage whenever they change localStorage&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;setItem&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;mosh-expense&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; JSON&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;stringify&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;expenses&lt;span style="color:#eceff4"&gt;));&lt;/span&gt; &lt;span style="color:#eceff4"&gt;},&lt;/span&gt; &lt;span style="color:#eceff4"&gt;[&lt;/span&gt;expenses&lt;span style="color:#eceff4"&gt;]);&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;return&lt;/span&gt; &lt;span style="color:#eceff4"&gt;(&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;&lt;/span&gt;AddForm expenses&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#eceff4"&gt;{&lt;/span&gt;expenses&lt;span style="color:#eceff4"&gt;}&lt;/span&gt; setExpenses&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#eceff4"&gt;{&lt;/span&gt;setExpenses&lt;span style="color:#eceff4"&gt;}&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;/&amp;gt;&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;&lt;/span&gt;hr &lt;span style="color:#81a1c1"&gt;/&amp;gt;&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;&lt;/span&gt;ExpenseList expenses&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#eceff4"&gt;{&lt;/span&gt;expenses&lt;span style="color:#eceff4"&gt;}&lt;/span&gt; setExpenses&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#eceff4"&gt;{&lt;/span&gt;setExpenses&lt;span style="color:#eceff4"&gt;}&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;/&amp;gt;&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;/&amp;gt;&lt;/span&gt; &lt;span style="color:#eceff4"&gt;);}&lt;/span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;export&lt;/span&gt; default App&lt;span style="color:#eceff4"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Because I&amp;rsquo;ve got my big boy TypeScript pants on, there&amp;rsquo;s a types.ts file with the types defined since they are common across a number of components:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;export&lt;/span&gt; interface Expense &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; id&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; string&lt;span style="color:#eceff4"&gt;;&lt;/span&gt; description&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; string&lt;span style="color:#eceff4"&gt;;&lt;/span&gt; amount&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; number&lt;span style="color:#eceff4"&gt;;&lt;/span&gt; category&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; string&lt;span style="color:#eceff4"&gt;;}&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;//&lt;/span&gt; needed so we can validate the form without the id then&lt;span style="color:#81a1c1"&gt;//&lt;/span&gt; add it laterexport interface FormExpense &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; description&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; string&lt;span style="color:#eceff4"&gt;;&lt;/span&gt; amount&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; number&lt;span style="color:#eceff4"&gt;;&lt;/span&gt; category&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; string&lt;span style="color:#eceff4"&gt;;}&lt;/span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;export&lt;/span&gt; interface ExpenseProps &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; expenses&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; Expense&lt;span style="color:#eceff4"&gt;[];&lt;/span&gt; setExpenses&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#eceff4"&gt;(&lt;/span&gt;expenses&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; Expense&lt;span style="color:#eceff4"&gt;[])&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;=&amp;gt;&lt;/span&gt; void&lt;span style="color:#eceff4"&gt;;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The reason for the existence of &lt;code&gt;FormExpense&lt;/code&gt; without the &lt;code&gt;id&lt;/code&gt; field is that the interaction between Zod and React got very complicated if the &lt;code&gt;id&lt;/code&gt; field existed - Zod was determined to validate it. If I left it out of the Zod schema it caused problems because the type and the schema didn&amp;rsquo;t match, if I made it optional in the schema, it created type mismatch problems that were solvable with typecasting calisthenics but it was ugly and difficult to follow. In the end, I went with these two types and just added the &lt;code&gt;id&lt;/code&gt; after validation.&lt;/p&gt;
&lt;p&gt;I also made a decision in coding the two types (&lt;code&gt;FormExpense&lt;/code&gt; and &lt;code&gt;Expense&lt;/code&gt;) like this. I&amp;rsquo;ve made a small footgun for a future developer in that they might add a new Field to one of them, and neglect to add it to the other. I could have avoided that by extending one, like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;export&lt;/span&gt; interface FormExpense &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; description&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; string&lt;span style="color:#eceff4"&gt;;&lt;/span&gt; amount&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; number&lt;span style="color:#eceff4"&gt;;&lt;/span&gt; category&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; string&lt;span style="color:#eceff4"&gt;;}&lt;/span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;export&lt;/span&gt; interface Expense &lt;span style="color:#81a1c1;font-weight:bold"&gt;extends&lt;/span&gt; FormExpense &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; id&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; string&lt;span style="color:#eceff4"&gt;;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That solves that problem, but to my mind is not as clear - conceptually, &lt;code&gt;Expense&lt;/code&gt; is the main thing here, and really &lt;code&gt;FormExpense&lt;/code&gt; is a derivative of it - just with the id removed. The TypeScript language designers could have helped me out here with some syntax, but I forgive you &lt;a href="https://en.wikipedia.org/wiki/Anders_Hejlsberg"&gt;Anders&lt;/a&gt; because of the good living I once made out of Delphi. In my alternative timeline TypeScript it could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;export&lt;/span&gt; interface Expense &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; description&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; string&lt;span style="color:#eceff4"&gt;;&lt;/span&gt; amount&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; number&lt;span style="color:#eceff4"&gt;;&lt;/span&gt; category&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; string&lt;span style="color:#eceff4"&gt;;&lt;/span&gt;  id&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; string&lt;span style="color:#eceff4"&gt;;}&lt;/span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;export&lt;/span&gt; interface FormExpense reduces Expense &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; id&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;%&lt;/span&gt;removed&lt;span style="color:#eceff4"&gt;;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So, I don&amp;rsquo;t love the duplication in the existing type definition, but it seemed like the lessor of two evils, the definitions are right next to each other, and we are working in TypeScript so the future developer will discover their mistake at the time they&amp;rsquo;ll make it.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s also a subtle design decision in the very simple &lt;code&gt;ExpenseProps&lt;/code&gt; worth thinking about. Firstly, I like having the props here with the &lt;code&gt;Expense&lt;/code&gt; definition, but mostly how simple they are. There&amp;rsquo;s no &lt;code&gt;addExpense()&lt;/code&gt;, &lt;code&gt;updateExpense()&lt;/code&gt;, or &lt;code&gt;deleteExpense()&lt;/code&gt;. That&amp;rsquo;s because, to the extent they are needed, they are dealt with inside each component.&lt;/p&gt;
&lt;p&gt;Components are already tightly bound to their data types, so we&amp;rsquo;re not making that worse by having this code with the compnent, but it would be perfectly valid to argue that all the data manipulation for expenses should be in one place. That argument would be won for me the second I needed to duplicate any of it - for example if two different components needed to delete an expense. But as the app stands now, this is neater, so that&amp;rsquo;s what I&amp;rsquo;ve gone with.&lt;/p&gt;
&lt;h3 id="expenses-list"&gt;Expenses List&lt;/h3&gt;
&lt;p&gt;The mechanism for the filter is that we have a local function that returns the filtered list based on the selected category which is a state variable of the &lt;code&gt;ExpensesList&lt;/code&gt; component. That &lt;code&gt;selectedCategory&lt;/code&gt; is updated from the onChange of the drop-down selector.&lt;/p&gt;
&lt;p&gt;The JSX just builds a table by .&lt;code&gt;map&lt;/code&gt;ping the filtered expenses. I don&amp;rsquo;t love the table code - it looks clunky. When I came back to look at it there were still some refactoring opportunities in it. Let&amp;rsquo;s have a look:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;table className=&amp;#34;table-bordered&amp;#34;&amp;gt; &amp;lt;thead&amp;gt; &amp;lt;tr&amp;gt; &amp;lt;th&amp;gt;Description&amp;lt;/th&amp;gt; &amp;lt;th&amp;gt;Amount&amp;lt;/th&amp;gt; &amp;lt;th&amp;gt;Category&amp;lt;/th&amp;gt; &amp;lt;th&amp;gt;&amp;lt;/th&amp;gt; &amp;lt;/tr&amp;gt; &amp;lt;/thead&amp;gt; &amp;lt;tbody&amp;gt; {filteredExpenses.map((expense) =&amp;gt; ( &amp;lt;tr key={expense.id}&amp;gt; &amp;lt;td className=&amp;#34;centred-td&amp;#34;&amp;gt;{expense.description}&amp;lt;/td&amp;gt; &amp;lt;td style={{ textAlign: &amp;#34;right&amp;#34; }}&amp;gt; $ {expense.amount.toLocaleString(&amp;#34;en-US&amp;#34;, { minimumFractionDigits: 2, maximumFractionDigits: 2, })} &amp;lt;/td&amp;gt; &amp;lt;td className=&amp;#34;centred-td&amp;#34;&amp;gt;{expense.category}&amp;lt;/td&amp;gt; &amp;lt;td className=&amp;#34;centred-td&amp;#34;&amp;gt; &amp;lt;button className=&amp;#34;btn btn-outline-danger&amp;#34; onClick={() =&amp;gt; handleDelete(expense.id)} &amp;gt; Delete &amp;lt;/button&amp;gt; &amp;lt;/td&amp;gt; &amp;lt;/tr&amp;gt; ))} &amp;lt;/tbody&amp;gt; &amp;lt;tfoot&amp;gt; &amp;lt;tr&amp;gt; &amp;lt;td&amp;gt;&amp;lt;/td&amp;gt; &amp;lt;td style={{ textAlign: &amp;#34;right&amp;#34;, fontWeight: &amp;#34;bold&amp;#34; }}&amp;gt; $ {filteredExpenses .reduce((total, expense) =&amp;gt; { return total + expense.amount; }, 0) .toLocaleString(&amp;#34;en-US&amp;#34;, { minimumFractionDigits: 2, maximumFractionDigits: 2, })} &amp;lt;/td&amp;gt; &amp;lt;td colSpan={2}&amp;gt;&amp;lt;/td&amp;gt; &amp;lt;/tr&amp;gt; &amp;lt;/tfoot&amp;gt;&amp;lt;/table&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The first thing I&amp;rsquo;m noticing is the code to format the amounts to US currency. That&amp;rsquo;s in there twice - once for each expense, and once for the total. We could write a function for that, but since we&amp;rsquo;re in React-land, let&amp;rsquo;s make it a component. Ideally we&amp;rsquo;d extract the user&amp;rsquo;s local currency from the browser settings somehow, but I don&amp;rsquo;t think that&amp;rsquo;s possible. In a real app, I guess we&amp;rsquo;d let them set it in settings. For the moment, they&amp;rsquo;ll just have to live in dollar land.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;interface TDCurrencyProps &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; children&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; number&lt;span style="color:#eceff4"&gt;;&lt;/span&gt; fontWeight&lt;span style="color:#bf616a"&gt;?&lt;/span&gt;&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; string&lt;span style="color:#eceff4"&gt;;&lt;/span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;function TDCurrency&lt;span style="color:#eceff4"&gt;({&lt;/span&gt;children&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; fontWeight &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#34;normal&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;}:&lt;/span&gt;TDCurrencyProps&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;return&lt;/span&gt; &lt;span style="color:#eceff4"&gt;(&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;&lt;/span&gt;td style&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#eceff4"&gt;{{&lt;/span&gt; textAlign&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#34;right&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; fontWeight&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; fontWeight &lt;span style="color:#eceff4"&gt;}}&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;$&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;children&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;toLocaleString&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;en-US&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; minimumFractionDigits&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#b48ead"&gt;2&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; maximumFractionDigits&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#b48ead"&gt;2&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; &lt;span style="color:#eceff4"&gt;})}&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;/&lt;/span&gt;td&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#eceff4"&gt;);}&lt;/span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;export&lt;/span&gt; default TDCurrency&lt;span style="color:#eceff4"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we&amp;rsquo;re thinking React-ivly! The table looks better already. Also, since now the alignment for the number column is in its own component, the centred styling can be applied to all the &lt;code&gt;&amp;lt;td&amp;gt;&lt;/code&gt;s so I can remove the class I had added for that:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;table className=&amp;#34;table-bordered&amp;#34;&amp;gt; &amp;lt;thead&amp;gt; &amp;lt;tr&amp;gt; &amp;lt;th&amp;gt;Description&amp;lt;/th&amp;gt; &amp;lt;th&amp;gt;Amount&amp;lt;/th&amp;gt; &amp;lt;th&amp;gt;Category&amp;lt;/th&amp;gt; &amp;lt;th&amp;gt;&amp;lt;/th&amp;gt; &amp;lt;/tr&amp;gt; &amp;lt;/thead&amp;gt; &amp;lt;tbody&amp;gt; {filteredExpenses.map((expense) =&amp;gt; ( &amp;lt;tr key={expense.id}&amp;gt; &amp;lt;td&amp;gt;{expense.description}&amp;lt;/td&amp;gt; &amp;lt;TDCurrency&amp;gt;{expense.amount}&amp;lt;/TDCurrency&amp;gt; &amp;lt;td&amp;gt;{expense.category}&amp;lt;/td&amp;gt; &amp;lt;td&amp;gt; &amp;lt;button className=&amp;#34;btn btn-outline-danger&amp;#34; onClick={() =&amp;gt; handleDelete(expense.id)} &amp;gt; Delete &amp;lt;/button&amp;gt; &amp;lt;/td&amp;gt; &amp;lt;/tr&amp;gt; ))} &amp;lt;/tbody&amp;gt; &amp;lt;tfoot&amp;gt; &amp;lt;tr&amp;gt; &amp;lt;td&amp;gt;&amp;lt;/td&amp;gt; &amp;lt;TDCurrency fontWeight=&amp;#34;bold&amp;#34;&amp;gt;{filteredTotal}&amp;lt;/TDCurrency&amp;gt; &amp;lt;td colSpan={2}&amp;gt;&amp;lt;/td&amp;gt; &amp;lt;/tr&amp;gt; &amp;lt;/tfoot&amp;gt;&amp;lt;/table&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Much nicer. Would it be crazy to component-ise that delete button as well? Probably, but while we&amp;rsquo;re on a roll, and this is starting to feel like fun.&lt;/p&gt;
&lt;p&gt;I really feel I&amp;rsquo;m starting to get the benefits of React that we&amp;rsquo;re paying for with the tooling complexity and larger bundle size.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;interface TDDeleteButtonProps &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; onClick&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#eceff4"&gt;(&lt;/span&gt;id&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; string&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;=&amp;gt;&lt;/span&gt; void&lt;span style="color:#eceff4"&gt;;&lt;/span&gt; id&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; string&lt;span style="color:#eceff4"&gt;;}&lt;/span&gt;function TDDeleteButton&lt;span style="color:#eceff4"&gt;({&lt;/span&gt; onClick&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; id&lt;span style="color:#eceff4"&gt;}:&lt;/span&gt; TDDeleteButtonProps&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;return&lt;/span&gt; &lt;span style="color:#eceff4"&gt;(&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;&lt;/span&gt;td&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;&lt;/span&gt;button className&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;btn btn-outline-danger&amp;#34;&lt;/span&gt; onClick&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#eceff4"&gt;{()&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;=&amp;gt;&lt;/span&gt; onClick&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;id&lt;span style="color:#eceff4"&gt;)}&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt; Delete &lt;span style="color:#81a1c1"&gt;&amp;lt;/&lt;/span&gt;button&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;/&lt;/span&gt;td&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#eceff4"&gt;);};&lt;/span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;export&lt;/span&gt; default TDDeleteButton&lt;span style="color:#eceff4"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I&amp;rsquo;m much happier with the table now:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;table className=&amp;#34;table-bordered&amp;#34;&amp;gt; &amp;lt;thead&amp;gt; &amp;lt;tr&amp;gt; &amp;lt;th&amp;gt;Description&amp;lt;/th&amp;gt; &amp;lt;th&amp;gt;Amount&amp;lt;/th&amp;gt; &amp;lt;th&amp;gt;Category&amp;lt;/th&amp;gt; &amp;lt;th&amp;gt;&amp;lt;/th&amp;gt; &amp;lt;/tr&amp;gt; &amp;lt;/thead&amp;gt; &amp;lt;tbody&amp;gt; {filteredExpenses.map((expense) =&amp;gt; ( &amp;lt;tr key={expense.id}&amp;gt; &amp;lt;td&amp;gt;{expense.description}&amp;lt;/td&amp;gt; &amp;lt;TDCurrency&amp;gt;{expense.amount}&amp;lt;/TDCurrency&amp;gt; &amp;lt;td&amp;gt;{expense.category}&amp;lt;/td&amp;gt; &amp;lt;TDDeleteButton onClick={handleDelete} id={expense.id}/&amp;gt; &amp;lt;/tr&amp;gt; ))} &amp;lt;/tbody&amp;gt; &amp;lt;tfoot&amp;gt; &amp;lt;tr&amp;gt; &amp;lt;td&amp;gt;&amp;lt;/td&amp;gt; &amp;lt;TDCurrency fontWeight=&amp;#34;bold&amp;#34;&amp;gt;{filteredTotal}&amp;lt;/TDCurrency&amp;gt; &amp;lt;td colSpan={2}&amp;gt;&amp;lt;/td&amp;gt; &amp;lt;/tr&amp;gt; &amp;lt;/tfoot&amp;gt;&amp;lt;/table&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The selector at the top of the expense list, is, well, okay.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;div className=&amp;#34;mb-3 category-selector&amp;#34;&amp;gt; &amp;lt;label htmlFor=&amp;#34;category&amp;#34; className=&amp;#34;form-label&amp;#34;&amp;gt; Category:{&amp;#34; &amp;#34;} &amp;lt;/label&amp;gt; &amp;lt;select id=&amp;#34;category&amp;#34; name=&amp;#34;category&amp;#34; className=&amp;#34;form-control drop-down&amp;#34; onChange={handleCategoryChange} &amp;gt; &amp;lt;option value=&amp;#34;All&amp;#34;&amp;gt;All&amp;lt;/option&amp;gt; &amp;lt;option value=&amp;#34;Groceries&amp;#34;&amp;gt;Groceries&amp;lt;/option&amp;gt; &amp;lt;option value=&amp;#34;Utilities&amp;#34;&amp;gt;Utilities&amp;lt;/option&amp;gt; &amp;lt;option value=&amp;#34;Entertainment&amp;#34;&amp;gt;Entertainment&amp;lt;/option&amp;gt; &amp;lt;/select&amp;gt;&amp;lt;/div&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The specification only specified these three categories - Groceries, Utilities, and Entertainment. and I have them hard-coded here, and in the add form. In Mosh&amp;rsquo;s version he&amp;rsquo;s made them a enum which is definitely nicer and future-proof-ier for a very small increase in complexity.&lt;/p&gt;
&lt;h3 id="mosh"&gt;Mosh&lt;/h3&gt;
&lt;p&gt;I really enjoy Mosh&amp;rsquo;s videos and courses. He generally puts the first hour of his &lt;a href="https://www.youtube.com/@programmingwithmosh"&gt;courses on Youtube&lt;/a&gt;, so you can try before you buy. He has a knack for anticipating the questions that occur to you as he&amp;rsquo;s explaining something so you feel a sense of continually being slightly challenged, but never overwhelmed. Hard recommend if you are learning programming.&lt;/p&gt;</description></item><item><title>CSS for React Components</title><link>https://blog.iankulin.com/css-for-react-components/</link><pubDate>Fri, 12 Jan 2024 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/css-for-react-components/</guid><description>&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2023-12-27-at-3.30.32-pm.jpg" alt=""&gt;
&lt;em&gt;Subscribe to my UX design course 😉&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;If you think back to HTML as being a document with headings and paragraphs and other semantic bits, it made a lot of sense to have the styles (expressed as CSS) separate to the document. This allows us to change the styles without touching the document - perhaps the user wanted a dark theme, needed the text bigger for accessibility, or perhaps the document was being consumed in some other way - for example a screen reader - so the styles were superfluous.&lt;/p&gt;
&lt;p&gt;In tension to this idea, is the idea that all the code related to a single thing should be encapsulated in one place. This is why we invented object orientated programming - we are creating such huge software systems that for a human to be able to maintain them, they need broken down into chunks that can be fully held in mind while we are working on them. Also, when we are talking about a modern single page application, we&amp;rsquo;ve come a long way from thinking of a web &amp;lsquo;page&amp;rsquo; as being a document to be passively consumed.&lt;/p&gt;
&lt;p&gt;Since the point of React is to create reusable &amp;lsquo;components&amp;rsquo; where the JS and HTML are written together, it&amp;rsquo;s reasonable to wonder if perhaps the CSS shouldn&amp;rsquo;t be in there too.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s look at a few different approaches to managing styles for our components in React.&lt;/p&gt;
&lt;h3 id="old-style-global"&gt;Old Style Global&lt;/h3&gt;
&lt;p&gt;Most times when I&amp;rsquo;m writing vanilla JS, if I&amp;rsquo;m not leveraging off &lt;a href="https://picocss.com/"&gt;Pico&lt;/a&gt; or &lt;a href="https://getbootstrap.com/"&gt;Bootstrap&lt;/a&gt; I have a single site-wide &lt;code&gt;styles.css&lt;/code&gt; file. Obviously this is going to be an option for a React app too. We&amp;rsquo;d just link it from the index.html in the root of our folder. Job&amp;rsquo;s a goodun.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://blog.iankulin.com/images/screen-shot-2023-12-27-at-3.27.51-pm.png"&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2023-12-27-at-3.27.51-pm.png" width="1000" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The most common downside of this approach, that I come across, is that I change the rest of the HTML through the various iterations, but never clean up the CSS. So I get left with bits of CSS that I&amp;rsquo;m not sure if they are being used somewhere - so I&amp;rsquo;m not brave enough to delete them because my UX testing is not good enough, so those fragments just end up sitting around forever.&lt;/p&gt;
&lt;h3 id="old-style-local"&gt;Old Style local&lt;/h3&gt;
&lt;p&gt;The first CSS we ever wrote was just stuffed in the HTML in style tags, and of course that&amp;rsquo;s still an option. We can use variables to help with the readability, and end up with something like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;function Card&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;props&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; CardProps&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;const&lt;/span&gt; cardStyles&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; React&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;CSSProperties &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; width&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#39;120px&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; boxShadow&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#39;0 4px 8px 0 rgba(0, 0, 0, 0.2)&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; textAlign&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#39;center&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; backgroundColor&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#39;cornsilk&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; margin&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#39;10px&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; padding&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#39;10px&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; transform&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#39;scale(1)&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; transition&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#39;transform 0.3s&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; &lt;span style="color:#eceff4"&gt;};&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;const&lt;/span&gt; pictureStyles&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; React&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;CSSProperties &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; width&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#39;100px&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; height&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#39;150px&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; margin&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#39;auto&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; borderRadius&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#39;50%&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; border&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#39;2px solid #000&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; &lt;span style="color:#eceff4"&gt;};&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;const&lt;/span&gt; nameStyles&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; React&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;CSSProperties &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; color&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#39;black&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; fontSize&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#39;18px&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; &lt;span style="color:#eceff4"&gt;};&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;return&lt;/span&gt; &lt;span style="color:#eceff4"&gt;(&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;&lt;/span&gt;div style&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#eceff4"&gt;{&lt;/span&gt;cardStyles&lt;span style="color:#eceff4"&gt;}&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;&lt;/span&gt;img style&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#eceff4"&gt;{&lt;/span&gt;pictureStyles&lt;span style="color:#eceff4"&gt;}&lt;/span&gt; src&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#eceff4"&gt;{&lt;/span&gt;props&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;image&lt;span style="color:#eceff4"&gt;}&lt;/span&gt; alt&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#eceff4"&gt;{&lt;/span&gt;props&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;name&lt;span style="color:#eceff4"&gt;}&lt;/span&gt; width&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#eceff4"&gt;{&lt;/span&gt;&lt;span style="color:#b48ead"&gt;100&lt;/span&gt;&lt;span style="color:#eceff4"&gt;}&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;/&amp;gt;&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;&lt;/span&gt;p style&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#eceff4"&gt;{&lt;/span&gt;nameStyles&lt;span style="color:#eceff4"&gt;}&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;{&lt;/span&gt;props&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;name&lt;span style="color:#eceff4"&gt;}&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;&amp;lt;/&lt;/span&gt;p&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;/&lt;/span&gt;div&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#eceff4"&gt;);}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Initially, this seemed like a good solution, but there&amp;rsquo;s a few bumps involved. The first is that, or course, this is not real CSS. You can see from the type that TypeScript forced me to use (React.CSSProperties) that these are React types that will get turned into CSS at some distant time in the future. Because of this, there are some oddities - my muscle memory wants to type the CSS property names like &lt;code&gt;box-shadow&lt;/code&gt;, but for this they need to be camel case.&lt;/p&gt;
&lt;p&gt;The next issue of &amp;lsquo;it&amp;rsquo;s not actually CSS&amp;rsquo; was when I wanted to do my hover effect for the cards. In CSS this is just:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.card:hover { transform: scale(1.05); background-color: lightgoldenrodyellow;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If I want to do that with inline styles, I need to capture the MouseEnter and MouseLeave events for the card, then swap the inline styles in and out in code. Ain&amp;rsquo;t nobody got the time for that.&lt;/p&gt;
&lt;h3 id="css--component-libraries"&gt;CSS &amp;amp; Component Libraries&lt;/h3&gt;
&lt;p&gt;A common and appealing answer to inline styles is to use CSS libraries such as BootStrap, TailWind etc. As well as allowing you to add complex styles into JSX with short memorable tags, they often enforce a design aesthetic without the developer having to put much thought into it. Since I have minimal design skills, that&amp;rsquo;s an appealing option.&lt;/p&gt;
&lt;p&gt;Taking this a step further are component are React component libraries such as &lt;a href="https://chakra-ui.com/"&gt;chakra&lt;/a&gt;, &lt;a href="https://mui.com/"&gt;Material UI&lt;/a&gt;, and &lt;a href="https://ant.design/components/overview"&gt;Ant&lt;/a&gt;. With these systems, you get styled components (that can be modified) that follow a unified design - you&amp;rsquo;re not really thinking of CSS but at a high level of abstraction.&lt;/p&gt;
&lt;h3 id="componentcss"&gt;component.css&lt;/h3&gt;
&lt;p&gt;I guess the default (since it&amp;rsquo;s generated like this in the create-app step) way of managing CSS closer to our components is to have a .CSS file for each component. This overcomes the &amp;lsquo;cleaning up&amp;rsquo; problem described above. If I&amp;rsquo;m deleting the NavBar component from this project, I can safely eliminate the NavBar.css file at the same time.&lt;/p&gt;
&lt;p&gt;The component CSS file is just imported at the top of the component file.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://blog.iankulin.com/images/screen-shot-2023-12-27-at-3.59.14-pm.png"&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2023-12-27-at-3.59.14-pm.png" width="1000" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If you build an app with the CSS in component.css files like this, all the CSS is combined into a single CSS file by the bundler. This raises the possibility of naming conflicts leading the hard to track down problems later. To avoid this I tend to use the component name as a class name and use that to tightly scope the CSS to avoid it bleeding out into other elements.&lt;/p&gt;
&lt;h3 id="modules"&gt;Modules&lt;/h3&gt;
&lt;p&gt;A bit fancier approach is to use CSS modules. This is somewhat similar to the component CSS files described above. Our CSS files have to be renamed to end in &lt;code&gt;.module.css&lt;/code&gt;, then in the code where you normally insert the class names, you use the class names from the styles library:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;import styles from &lt;span style="color:#a3be8c"&gt;&amp;#34;./Card.module.css&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;;&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;//&lt;/span&gt; props &lt;span style="color:#81a1c1;font-weight:bold"&gt;for&lt;/span&gt; cardinterface CardProps &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; name&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; string&lt;span style="color:#eceff4"&gt;;&lt;/span&gt; image&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; string&lt;span style="color:#eceff4"&gt;;}&lt;/span&gt;function Card&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;props&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; CardProps&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;return&lt;/span&gt; &lt;span style="color:#eceff4"&gt;(&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;&lt;/span&gt;div className&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#eceff4"&gt;{&lt;/span&gt;styles&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;card&lt;span style="color:#eceff4"&gt;}&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;&lt;/span&gt;img className&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#eceff4"&gt;{&lt;/span&gt;styles&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;picture&lt;span style="color:#eceff4"&gt;}&lt;/span&gt; src&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#eceff4"&gt;{&lt;/span&gt;props&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;image&lt;span style="color:#eceff4"&gt;}&lt;/span&gt; alt&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#eceff4"&gt;{&lt;/span&gt;props&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;name&lt;span style="color:#eceff4"&gt;}&lt;/span&gt; width&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#eceff4"&gt;{&lt;/span&gt;&lt;span style="color:#b48ead"&gt;100&lt;/span&gt;&lt;span style="color:#eceff4"&gt;}&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;/&amp;gt;&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;&lt;/span&gt;p className&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#eceff4"&gt;{&lt;/span&gt;styles&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;name&lt;span style="color:#eceff4"&gt;}&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;{&lt;/span&gt;props&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;name&lt;span style="color:#eceff4"&gt;}&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;&amp;lt;/&lt;/span&gt;p&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;/&lt;/span&gt;div&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#eceff4"&gt;);}&lt;/span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;export&lt;/span&gt; default Card&lt;span style="color:#eceff4"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Those class names match the CSS:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.card { width: 120px; box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2); text-align: center; background-color: cornsilk; margin: 10px; padding: 10px; /* tilt and enlarge on mouseover */ transform: scale(1); transition: transform 0.3s;}.card:hover { transform: scale(1.05); background-color: lightgoldenrodyellow;}.name { font-size: 18px; color: black;}.picture { width: 100px; height: 150px; margin: auto; border-radius: 50%; border: 2px solid black;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The advantage of using modules, is that you now do &lt;em&gt;not&lt;/em&gt; have worry about name clashes. If I build the app and have a look in the generated CSS file, you can see how that works:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;._card_j2a8o_2 { width: 120px; box-shadow: 0 4px 8px #0003; text-align: center; background-color: #fff8dc; margin: 10px; padding: 10px; transform: scale(1); transition: transform 0.3s;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;All the generated CSS has unique class names created for it! Presumably these match the ones being used in the JSX, and this explains why you need to use the &lt;code&gt;styles.&lt;/code&gt; names in your components.&lt;/p&gt;
&lt;h3 id="styled-components"&gt;Styled Components&lt;/h3&gt;
&lt;p&gt;There are several libraries that tackle the issue of what to do about CSS in React. One of the more popular ones is &lt;code&gt;Styled Components&lt;/code&gt;. How this works is that you define a base component with some styles in it (with a weird backticky syntax), then build your own components from the styled one. It makes more sense when you see it than my written explanation.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;import styled from &lt;span style="color:#a3be8c"&gt;&amp;#34;styled-components&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;;&lt;/span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;const&lt;/span&gt; StyledDiv &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; styled&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;div&lt;span style="color:#bf616a"&gt;`&lt;/span&gt; width&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#b48ead"&gt;120&lt;/span&gt;px&lt;span style="color:#eceff4"&gt;;&lt;/span&gt; box&lt;span style="color:#81a1c1"&gt;-&lt;/span&gt;shadow&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#b48ead"&gt;0&lt;/span&gt; &lt;span style="color:#b48ead"&gt;4&lt;/span&gt;px &lt;span style="color:#b48ead"&gt;8&lt;/span&gt;px &lt;span style="color:#b48ead"&gt;0&lt;/span&gt; rgba&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#b48ead"&gt;0&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; &lt;span style="color:#b48ead"&gt;0&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; &lt;span style="color:#b48ead"&gt;0&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; &lt;span style="color:#b48ead"&gt;0.2&lt;/span&gt;&lt;span style="color:#eceff4"&gt;);&lt;/span&gt; text&lt;span style="color:#81a1c1"&gt;-&lt;/span&gt;align&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; center&lt;span style="color:#eceff4"&gt;;&lt;/span&gt; background&lt;span style="color:#81a1c1"&gt;-&lt;/span&gt;color&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; cornsilk&lt;span style="color:#eceff4"&gt;;&lt;/span&gt; margin&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#b48ead"&gt;10&lt;/span&gt;px&lt;span style="color:#eceff4"&gt;;&lt;/span&gt; padding&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#b48ead"&gt;10&lt;/span&gt;px&lt;span style="color:#eceff4"&gt;;&lt;/span&gt; transform&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; scale&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#b48ead"&gt;1&lt;/span&gt;&lt;span style="color:#eceff4"&gt;);&lt;/span&gt; transition&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; transform &lt;span style="color:#b48ead"&gt;0.3&lt;/span&gt;s&lt;span style="color:#eceff4"&gt;;&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;amp;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;:&lt;/span&gt;hover &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; transform&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; scale&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#b48ead"&gt;1.05&lt;/span&gt;&lt;span style="color:#eceff4"&gt;);&lt;/span&gt; background&lt;span style="color:#81a1c1"&gt;-&lt;/span&gt;color&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; lightgoldenrodyellow&lt;span style="color:#eceff4"&gt;;&lt;/span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;&lt;span style="color:#bf616a"&gt;`&lt;/span&gt;&lt;span style="color:#eceff4"&gt;;&lt;/span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;const&lt;/span&gt; StyledImage &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; styled&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;img&lt;span style="color:#bf616a"&gt;`&lt;/span&gt; width&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#b48ead"&gt;100&lt;/span&gt;px&lt;span style="color:#eceff4"&gt;;&lt;/span&gt; height&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#b48ead"&gt;150&lt;/span&gt;px&lt;span style="color:#eceff4"&gt;;&lt;/span&gt; margin&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; auto&lt;span style="color:#eceff4"&gt;;&lt;/span&gt; border&lt;span style="color:#81a1c1"&gt;-&lt;/span&gt;radius&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#b48ead"&gt;50&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;%&lt;/span&gt;&lt;span style="color:#eceff4"&gt;;&lt;/span&gt; border&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#b48ead"&gt;2&lt;/span&gt;px solid black&lt;span style="color:#eceff4"&gt;;&lt;/span&gt;&lt;span style="color:#bf616a"&gt;`&lt;/span&gt;&lt;span style="color:#eceff4"&gt;;&lt;/span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;const&lt;/span&gt; StyledP &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; styled&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;p&lt;span style="color:#bf616a"&gt;`&lt;/span&gt; font&lt;span style="color:#81a1c1"&gt;-&lt;/span&gt;size&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#b48ead"&gt;18&lt;/span&gt;px&lt;span style="color:#eceff4"&gt;;&lt;/span&gt; color&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; black&lt;span style="color:#eceff4"&gt;;&lt;/span&gt;&lt;span style="color:#bf616a"&gt;`&lt;/span&gt;&lt;span style="color:#eceff4"&gt;;&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;//&lt;/span&gt; props &lt;span style="color:#81a1c1;font-weight:bold"&gt;for&lt;/span&gt; cardinterface CardProps &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; name&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; string&lt;span style="color:#eceff4"&gt;;&lt;/span&gt; image&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; string&lt;span style="color:#eceff4"&gt;;}&lt;/span&gt;function Card&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;props&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; CardProps&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;return&lt;/span&gt; &lt;span style="color:#eceff4"&gt;(&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;&lt;/span&gt;StyledDiv className&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;card&amp;#34;&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;&lt;/span&gt;StyledImage className&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;picture&amp;#34;&lt;/span&gt; src&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#eceff4"&gt;{&lt;/span&gt;props&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;image&lt;span style="color:#eceff4"&gt;}&lt;/span&gt; alt&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#eceff4"&gt;{&lt;/span&gt;props&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;name&lt;span style="color:#eceff4"&gt;}&lt;/span&gt; width&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#eceff4"&gt;{&lt;/span&gt;&lt;span style="color:#b48ead"&gt;100&lt;/span&gt;&lt;span style="color:#eceff4"&gt;}&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;/&amp;gt;&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;&lt;/span&gt;StyledP className&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;{&lt;/span&gt;props&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;name&lt;span style="color:#eceff4"&gt;}&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;&amp;lt;/&lt;/span&gt;StyledP&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;/&lt;/span&gt;StyledDiv&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#eceff4"&gt;);}&lt;/span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;export&lt;/span&gt; default Card&lt;span style="color:#eceff4"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you don&amp;rsquo;t mind more dependencies, this is a great solution - I like the clarity of the philosophy of creating styled versions of regular elements as React elements, then using them as the building blocks of our new component. All the CSS attributes you&amp;rsquo;ve learned are still there.&lt;/p&gt;
&lt;p&gt;The only thing I needed to look up was how to deal with my hover. In the CSS this had been:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.card { width: 120px; box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2); text-align: center; background-color: cornsilk; margin: 10px; padding: 10px; transform: scale(1); transition: transform 0.3s;}.card:hover { transform: scale(1.05); background-color: lightgoldenrodyellow;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Which had to be translated into this with the &amp;amp; syntax:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;const&lt;/span&gt; StyledDiv &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; styled&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;div&lt;span style="color:#bf616a"&gt;`&lt;/span&gt; width&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#b48ead"&gt;120&lt;/span&gt;px&lt;span style="color:#eceff4"&gt;;&lt;/span&gt; box&lt;span style="color:#81a1c1"&gt;-&lt;/span&gt;shadow&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#b48ead"&gt;0&lt;/span&gt; &lt;span style="color:#b48ead"&gt;4&lt;/span&gt;px &lt;span style="color:#b48ead"&gt;8&lt;/span&gt;px &lt;span style="color:#b48ead"&gt;0&lt;/span&gt; rgba&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#b48ead"&gt;0&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; &lt;span style="color:#b48ead"&gt;0&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; &lt;span style="color:#b48ead"&gt;0&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; &lt;span style="color:#b48ead"&gt;0.2&lt;/span&gt;&lt;span style="color:#eceff4"&gt;);&lt;/span&gt; text&lt;span style="color:#81a1c1"&gt;-&lt;/span&gt;align&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; center&lt;span style="color:#eceff4"&gt;;&lt;/span&gt; background&lt;span style="color:#81a1c1"&gt;-&lt;/span&gt;color&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; cornsilk&lt;span style="color:#eceff4"&gt;;&lt;/span&gt; margin&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#b48ead"&gt;10&lt;/span&gt;px&lt;span style="color:#eceff4"&gt;;&lt;/span&gt; padding&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#b48ead"&gt;10&lt;/span&gt;px&lt;span style="color:#eceff4"&gt;;&lt;/span&gt; transform&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; scale&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#b48ead"&gt;1&lt;/span&gt;&lt;span style="color:#eceff4"&gt;);&lt;/span&gt; transition&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; transform &lt;span style="color:#b48ead"&gt;0.3&lt;/span&gt;s&lt;span style="color:#eceff4"&gt;;&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;amp;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;:&lt;/span&gt;hover &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; transform&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; scale&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#b48ead"&gt;1.05&lt;/span&gt;&lt;span style="color:#eceff4"&gt;);&lt;/span&gt; background&lt;span style="color:#81a1c1"&gt;-&lt;/span&gt;color&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; lightgoldenrodyellow&lt;span style="color:#eceff4"&gt;;&lt;/span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;&lt;span style="color:#bf616a"&gt;`&lt;/span&gt;&lt;span style="color:#eceff4"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So - lots of options for dealing with CSS in React. I haven&amp;rsquo;t written much, so I&amp;rsquo;m not sure what my personal preference is. styled-components is definitely the most elegant of the approaches I&amp;rsquo;ve looked at here, but I&amp;rsquo;m a dependency calorie counter so my natural inclination is look elsewhere. The CSS file for each component seems like the next best system - I like that there&amp;rsquo;s no special hooks in the JSX besides the class names I would have used in ordinary HTML, but then you have the risk of name clashes. They can be avoided with the modules - but I am sort of used to dealing with that anyway.&lt;/p&gt;</description></item><item><title>React - a To Do Example</title><link>https://blog.iankulin.com/react-a-to-do-example/</link><pubDate>Mon, 08 Jan 2024 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/react-a-to-do-example/</guid><description>&lt;p&gt;Since I&amp;rsquo;m on a roll making different versions of the To Do app, this might be a good time to talk about &lt;a href="https://react.dev/"&gt;React&lt;/a&gt;. React is one of the giants of front end libraries. It&amp;rsquo;s based on a few big ideas - and to work effectively in React you need to wrap your head around these.&lt;/p&gt;
&lt;h3 id="overview"&gt;Overview&lt;/h3&gt;
&lt;p&gt;Components - when you are developing in React, the starting point of your build is to decompose the user interface in to logical pieces. These components (comprising a mixture of HTML and Javascript) will be the building blocks of your app. In a good composable architecture components are reusable, and that is true for React (there are several sources of components you can pull in). For example, if you created some sort of special slider for your app, it is possible to reuse that quite easily.&lt;/p&gt;
&lt;p&gt;Declarative - this was one of the big barriers when I was learning SwiftUI. The UI is just described. It&amp;rsquo;s not a big step when you&amp;rsquo;re coming from HTML - all that is is a description of the user interface. The next step of this is that React deals with using the state of your data model to update the UI. This means that these state-UI connections are made very explicit (which I like) and protected. For example if there&amp;rsquo;s a counter on a web page, you can&amp;rsquo;t change the HTML of the page to increment the counter, and in fact, you can&amp;rsquo;t directly change the counter. These things are wrapped up so React can manage them, and you have to play by React&amp;rsquo;s rules.&lt;/p&gt;
&lt;p&gt;Virtual DOM - since each component knows what state it depends upon, and changing that state causes the component to be redrawn, it quickly gets expensive with parts of the page being reloaded. To improve performance, and reduce often unneeded flashes of content loading, React keeps a copy of the DOM and makes changes to it rather than the real DOM. Since this is instrumented, React can easily see what changes &lt;em&gt;really&lt;/em&gt; need to be percolated to the browser DOM and can manage how that happens in the most efficient way. Sometimes I think I know what I want to happen for something to be efficient but in React, it&amp;rsquo;s often not in your control, and you just need to trust the system.&lt;/p&gt;
&lt;h3 id="tooling"&gt;Tooling&lt;/h3&gt;
&lt;p&gt;We&amp;rsquo;re leaving my comfort zone of straightforward development environments, along with the major benefit of working in Javascript. The complexity of the React system requires a build step to produce the production artifacts. Whether you use the standard &lt;a href="https://create-react-app.dev/"&gt;Create-React&lt;/a&gt;, or &lt;a href="https://vitejs.dev/"&gt;Vite&lt;/a&gt; (as I did for this project) you get a system for bundling the code, mapping it (since for debugging you need a way to translate the source line that&amp;rsquo;s running back to the human readable source), and a development server to run the app while you&amp;rsquo;re working on it.&lt;/p&gt;
&lt;p&gt;These things inevitably add to complexity and errors, and are the reason that big projects start to need tools like development containers to remove a pain point. Like anything, you get better with experience, but especially at the start there&amp;rsquo;s a developer cost involved. React is incredibly popular, so most people&amp;rsquo;s calculus on this is that the extra complexity in project management is made up for with improved developer experience.&lt;/p&gt;
&lt;p&gt;In any case, I got started by typing &lt;code&gt;npm create vite@latest .&lt;/code&gt; and then choosing React and Javascript. This created a starter project, and spun it up in the development server. In the &lt;code&gt;package.json&lt;/code&gt; file it gives you, there is a build command that can be run with &lt;code&gt;npm run build&lt;/code&gt; to create the output files. I had issues with the development server that serves up these frontend files - since I also needed to run a real server (the unchanged REST API server for Todo Items data from the earlier project) on a different port. Then it complained - thinking it was part of a cross-site scripting hack. To overcome it, I just built the production code for each round of testing - with such a little project this wasn&amp;rsquo;t a hardship. But when I did have an error to track, it pointed me to a line number that turned out to be about 20 pages of minified code.&lt;/p&gt;
&lt;h3 id="components"&gt;Components&lt;/h3&gt;
&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2023-12-22-at-7.47.24-am.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;Just a little reminder of how this app looks so we can think about how we&amp;rsquo;re going to break it down. There is a list of todo items, each one has a button to delete it (which we do when it has been completed) then at the bottom there&amp;rsquo;s a little form to add a new item (by clicking the button or hitting enter) to the bottom of the list.&lt;/p&gt;
&lt;p&gt;I ended up with three components - the App (every React app has one), the TodoList, and the AddTodoForm. Note that I could easily have had a forth component - the TodoItem. This is a bit of matter of taste - I probably would have if I wanted to do something fancier like editing in place - but for the current UX the cost of extracting out another component wasn&amp;rsquo;t worth the benefit.&lt;/p&gt;
&lt;h3 id="anatomy-of-a-component"&gt;Anatomy of a component&lt;/h3&gt;
&lt;p&gt;I claimed earlier that a component was it&amp;rsquo;s HTML and Javascript wrapped up together which, while a massive simpliciation, is a good place to start thinking about it. Every component is just a function that returns a bunch of (templated) HTML. We&amp;rsquo;ll start off by developing our AddTodoForm. At it&amp;rsquo;s simplest, it could be something like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;function AddTodoForm&lt;span style="color:#eceff4"&gt;()&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;return&lt;/span&gt; &lt;span style="color:#eceff4"&gt;(&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;&lt;/span&gt;form&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;&lt;/span&gt;input type&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;text&amp;#34;&lt;/span&gt; name&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;todo_item&amp;#34;&lt;/span&gt; id&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;todo&amp;#34;&lt;/span&gt; required &lt;span style="color:#81a1c1"&gt;/&amp;gt;&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;&lt;/span&gt;button type&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;submit&amp;#34;&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt;Add&lt;span style="color:#81a1c1"&gt;&amp;lt;/&lt;/span&gt;button&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;/&lt;/span&gt;form&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#eceff4"&gt;);}&lt;/span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;export&lt;/span&gt; default AddTodoForm&lt;span style="color:#eceff4"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;But this little form component can&amp;rsquo;t really talk to the world, where as we need it to add a todo item. First, let&amp;rsquo;s track any changes to the text field.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;import &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; useState &lt;span style="color:#eceff4"&gt;}&lt;/span&gt; from &lt;span style="color:#a3be8c"&gt;&amp;#39;react&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;;&lt;/span&gt;function AddTodoForm&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;props&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;const&lt;/span&gt; &lt;span style="color:#eceff4"&gt;[&lt;/span&gt;value&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; setValue&lt;span style="color:#eceff4"&gt;]&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; useState&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;);&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;return&lt;/span&gt; &lt;span style="color:#eceff4"&gt;(&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;&lt;/span&gt;form onSubmit&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#eceff4"&gt;{&lt;/span&gt;handleSubmit&lt;span style="color:#eceff4"&gt;}&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;&lt;/span&gt;input type&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;text&amp;#34;&lt;/span&gt; name&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;todo_item&amp;#34;&lt;/span&gt; id&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;todo&amp;#34;&lt;/span&gt; value&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#eceff4"&gt;{&lt;/span&gt;value&lt;span style="color:#eceff4"&gt;}&lt;/span&gt; onChange&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#eceff4"&gt;{&lt;/span&gt;e &lt;span style="color:#81a1c1"&gt;=&amp;gt;&lt;/span&gt; setValue&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;e&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;target&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;value&lt;span style="color:#eceff4"&gt;)}&lt;/span&gt; required &lt;span style="color:#81a1c1"&gt;/&amp;gt;&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;&lt;/span&gt;button type&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;submit&amp;#34;&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt;Add&lt;span style="color:#81a1c1"&gt;&amp;lt;/&lt;/span&gt;button&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;/&lt;/span&gt;form&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#eceff4"&gt;);}&lt;/span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;export&lt;/span&gt; default AddTodoForm&lt;span style="color:#eceff4"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is an example of the very explicit management of state I was talking about earlier.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;const [value, setValue] = useState('');&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;useState() is a React hook for managing state. This line gives us a getter (value) and setter (setValue) for this variable, and set&amp;rsquo;s its initial state to &amp;lsquo;&amp;rsquo;. If the value changes the component will be redrawn. React will know that the value has changed as this is built into the setValue() function where we never need see or worry about it. If you foolishly decided to side-step React and assign directly to &lt;code&gt;value&lt;/code&gt;, I guess there&amp;rsquo;d be a runtime error, or even worse, no error and the management of the DOM state wuld fall into some type of chaos.&lt;/p&gt;
&lt;p&gt;What we&amp;rsquo;re doing with this value (which I&amp;rsquo;m now realising is very badly named) is using it to collect the text input from out form. It&amp;rsquo;s constantly updated as the user types.&lt;/p&gt;
&lt;p&gt;onChange={e =&amp;gt; setValue(e.target.value)}&lt;/p&gt;
&lt;p&gt;So that&amp;rsquo;s our value sorted, but of course we need to add it to our data model. This model is being managed at the App level so there&amp;rsquo;s a bit of juggling needed to get it out to there. This &amp;lsquo;plumbing&amp;rsquo; cost is the downside of these types or framework, but it&amp;rsquo;s not really complex and quickly becomes routine.&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;ll start at the top, here&amp;rsquo;s the relevant code from App.jsx - our App component.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;const&lt;/span&gt; &lt;span style="color:#eceff4"&gt;[&lt;/span&gt;todos&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; setTodos&lt;span style="color:#eceff4"&gt;]&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; useState&lt;span style="color:#eceff4"&gt;([]);&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;const&lt;/span&gt; addTodo &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; &lt;span style="color:#eceff4"&gt;(&lt;/span&gt;newTodo&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; fetch&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;http://localhost:3000/todos&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; method&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#39;POST&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; headers&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#39;Content-Type&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#39;application/json&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; &lt;span style="color:#eceff4"&gt;},&lt;/span&gt; body&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; JSON&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;stringify&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;newTodo&lt;span style="color:#eceff4"&gt;),&lt;/span&gt; &lt;span style="color:#eceff4"&gt;})&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;then&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;response &lt;span style="color:#81a1c1"&gt;=&amp;gt;&lt;/span&gt; response&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;json&lt;span style="color:#eceff4"&gt;())&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;then&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;data &lt;span style="color:#81a1c1"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;//&lt;/span&gt; Update the todos state with the new todo setTodos&lt;span style="color:#eceff4"&gt;([&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;...&lt;/span&gt;todos&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; data&lt;span style="color:#eceff4"&gt;]);&lt;/span&gt; &lt;span style="color:#eceff4"&gt;});&lt;/span&gt; &lt;span style="color:#eceff4"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This useState hook should be starting to look familiar. We read the todos from &lt;code&gt;todos&lt;/code&gt;, and write to them with &lt;code&gt;setTodos()&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;addTodo&lt;/code&gt; does the actual work of saving it to the database (via our REST API) then creates a new &lt;code&gt;todos&lt;/code&gt; array by adding our new one to the end. But we need to pass this down into our AddTodoForm. Here&amp;rsquo;s the main part of the App that returns out HTML, in this case that&amp;rsquo;s the list of todos and our form:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; return ( &amp;lt;main&amp;gt; &amp;lt;h1&amp;gt;To do&amp;lt;/h1&amp;gt; &amp;lt;TodoList todos={todos} onDeleteTodo={deleteTodo}/&amp;gt; &amp;lt;AddTodoForm onAddTodo={addTodo} /&amp;gt; &amp;lt;/main&amp;gt; )
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can see here that the todos array and a function &lt;em&gt;deleteTodo&lt;/em&gt;() are passed into the the TodoList component, and that our &lt;em&gt;addTodo()&lt;/em&gt; function is passed to the AddTodoForm.&lt;/p&gt;
&lt;p&gt;In React, things like this that are passed into a component are passed as a single object variable called &amp;lsquo;props&amp;rsquo; - short for properties. It seems crazy to me to be bundling them like this in a language in 2023 rather than passing them explicitly as separate variables. This lack or clarity about what&amp;rsquo;s being passed into a component is doubtless one of the reasons TypeScript is such a common combo with React. It&amp;rsquo;s certainly the first time I&amp;rsquo;ve felt the need of it.&lt;/p&gt;
&lt;p&gt;There is a lighter partial solution to adding types to props so that the linter can call out any issues - this is the PropTypes library - once it&amp;rsquo;s installed, we import it with &lt;code&gt;import PropTypes from 'prop-types';&lt;/code&gt; then we can add a definition for the props at the bottom of the file. For our AddTodoForm, this would look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;AddTodoForm.propTypes = { onAddTodo: PropTypes.func.isRequired,};
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now the linter will prevent us from using anything other than props.onAddToDo, and it will flag if it&amp;rsquo;s being used as anything other than a function.&lt;/p&gt;
&lt;p&gt;Anyway, the props are passed in and we can extract the function from it to use.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;import &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; useState &lt;span style="color:#eceff4"&gt;}&lt;/span&gt; from &lt;span style="color:#a3be8c"&gt;&amp;#39;react&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;;&lt;/span&gt;function AddTodoForm&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;props&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;const&lt;/span&gt; &lt;span style="color:#eceff4"&gt;[&lt;/span&gt;value&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; setValue&lt;span style="color:#eceff4"&gt;]&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; useState&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;);&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;const&lt;/span&gt; handleSubmit &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; &lt;span style="color:#eceff4"&gt;(&lt;/span&gt;event&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; event&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;preventDefault&lt;span style="color:#eceff4"&gt;();&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;if&lt;/span&gt; &lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;!&lt;/span&gt;value&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;return&lt;/span&gt;&lt;span style="color:#eceff4"&gt;;&lt;/span&gt; props&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;onAddTodo&lt;span style="color:#eceff4"&gt;({&lt;/span&gt; todo_item&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; value &lt;span style="color:#eceff4"&gt;});&lt;/span&gt; setValue&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;);&lt;/span&gt; &lt;span style="color:#eceff4"&gt;};&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;return&lt;/span&gt; &lt;span style="color:#eceff4"&gt;(&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;&lt;/span&gt;form onSubmit&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#eceff4"&gt;{&lt;/span&gt;handleSubmit&lt;span style="color:#eceff4"&gt;}&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;&lt;/span&gt;input type&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;text&amp;#34;&lt;/span&gt; name&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;todo_item&amp;#34;&lt;/span&gt; id&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;todo&amp;#34;&lt;/span&gt; value&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#eceff4"&gt;{&lt;/span&gt;value&lt;span style="color:#eceff4"&gt;}&lt;/span&gt; onChange&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#eceff4"&gt;{&lt;/span&gt;e &lt;span style="color:#81a1c1"&gt;=&amp;gt;&lt;/span&gt; setValue&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;e&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;target&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;value&lt;span style="color:#eceff4"&gt;)}&lt;/span&gt; required &lt;span style="color:#81a1c1"&gt;/&amp;gt;&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;&lt;/span&gt;button type&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;submit&amp;#34;&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt;Add&lt;span style="color:#81a1c1"&gt;&amp;lt;/&lt;/span&gt;button&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;/&lt;/span&gt;form&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#eceff4"&gt;);}&lt;/span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;export&lt;/span&gt; default AddTodoForm&lt;span style="color:#eceff4"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The arrangement of elements in this file will start to become familiar - there&amp;rsquo;s often some state at the top with the useState() hook, then a few handler functions, then our final return of the &amp;lsquo;HTML&amp;rsquo;.&lt;/p&gt;
&lt;p&gt;Our TodoList is a bit simpler in that it doesn&amp;rsquo;t have any handlers, but a better illustration of using PropTypes since it has access to the global state of the todos.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;import PropTypes from &lt;span style="color:#a3be8c"&gt;&amp;#39;prop-types&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;;&lt;/span&gt;function TodoList&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;props&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;return&lt;/span&gt; &lt;span style="color:#eceff4"&gt;(&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;&lt;/span&gt;ul&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;props&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;todos&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;map&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;todo &lt;span style="color:#81a1c1"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#eceff4"&gt;(&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;&lt;/span&gt;li key&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#eceff4"&gt;{&lt;/span&gt;todo&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;id&lt;span style="color:#eceff4"&gt;}&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;todo&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;todo_item&lt;span style="color:#eceff4"&gt;}&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;&lt;/span&gt;button onClick&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#eceff4"&gt;{()&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;=&amp;gt;&lt;/span&gt; props&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;onDeleteTodo&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;todo&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;id&lt;span style="color:#eceff4"&gt;)}&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt; Done &lt;span style="color:#81a1c1"&gt;&amp;lt;/&lt;/span&gt;button&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;/&lt;/span&gt;li&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#eceff4"&gt;))}&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;/&lt;/span&gt;ul&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#eceff4"&gt;)}&lt;/span&gt;TodoList&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;propTypes &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; todos&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; PropTypes&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;array&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;isRequired&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; onDeleteTodo&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; PropTypes&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;func&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;isRequired&lt;span style="color:#eceff4"&gt;};&lt;/span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;export&lt;/span&gt; default TodoList&lt;span style="color:#eceff4"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="conclusion"&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;You would not sensibly reach for React for a project this size - the complexity of the tooling, and the fact that we&amp;rsquo;re now shipping 150K of Javascript to do something we were nicely achieving in 2K of vanilla, or 15K with htmx makes me deeply uncomfortable. Nevertheless, hundreds of thousands of developers can&amp;rsquo;t be wrong - React&amp;rsquo;s component model is a powerful one for building modern single page applications, especially when it allows you to pull in components from public or corporate collections.&lt;/p&gt;
&lt;p&gt;I plan on doing some more React - partly because its just such big part of the webdev world, and partly because I&amp;rsquo;d like to get some experience with TypeScript, so I&amp;rsquo;m fishing around for a medium size project to play with both of these technologies.&lt;/p&gt;
&lt;p&gt;(&lt;a href="https://github.com/IanKulin/todo/tree/react"&gt;source&lt;/a&gt;)&lt;/p&gt;</description></item><item><title>Expired Packages Part II</title><link>https://blog.iankulin.com/expired-packages-part-ii/</link><pubDate>Tue, 31 Jan 2023 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/expired-packages-part-ii/</guid><description>&lt;p&gt;Following on from the previous post&amp;hellip;&lt;/p&gt;
&lt;p&gt;I went the nuclear route - deleted the node_modules folder, package-lock.json and installed the packages from packages.json. I still had some errors, but the react app at least ran correctly. Also, the messages are a bit more intelligible, and all of them cascade from this one.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;# npm audit report
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;nth-check &amp;lt;2.0.1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Severity: high
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Inefficient Regular Expression Complexity in nth-check - https://github.com/advisories/GHSA-rp65-9cf3-cjxr
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;fix available via `npm audit fix --force`
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Will install react-scripts@2.1.3, which is a breaking change
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;node_modules/svgo/node_modules/nth-check
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;From my, admittedly ignorant, viewpoint, there&amp;rsquo;s a couple of weird things going on here.&lt;/p&gt;
&lt;p&gt;The first is how the hell is installing &lt;a href="mailto:react-scripts@2.1.3"&gt;react-scripts@2.1.3&lt;/a&gt; a good idea, when the &lt;a href="https://www.npmjs.com/package/react-scripts"&gt;current version is 5.0.1&lt;/a&gt;. That does not seem like a good solution.&lt;/p&gt;
&lt;p&gt;The second is that is that the currently installed version of nth-check seems like it is 2.1.1 which is the current version, and certainly &amp;gt;2.0.1 which is the complaint. My basis for this claim is this encouraging part of package-lock.json:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2023-01-23-at-1.39.10-pm.jpg" alt=""&gt;&lt;/p&gt;
&lt;p&gt;But if I check the installed version using &lt;code&gt;npm list nth-check&lt;/code&gt;, I get this bad news:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2023-01-23-at-1.58.13-pm.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;So one version of css-select is using an old version of nth-check, likely this is the source of my troubles.&lt;/p&gt;
&lt;p&gt;As far as I can make out, the package-lock.json file&amp;rsquo;s purpose is to lock in particular versions of packages. If it&amp;rsquo;s committed with the rest of your code, it guarantees that when you rebuild the app, it will be the same as the one committed, without the need commit all the node modules you are depending on. Generally I don&amp;rsquo;t think you are meant to directly edit it, but it suddenly seems like a good idea.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s about five dependencies on this package in package-lock.json, and I notice all of them except this one, start with the ^caret.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;#34;nth-check&amp;#34;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;#34;version&amp;#34;: &amp;#34;1.0.2&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;#34;resolved&amp;#34;: &amp;#34;https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;#34;integrity&amp;#34;: &amp;#34;sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;#34;requires&amp;#34;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;#34;boolbase&amp;#34;: &amp;#34;~1.0.0&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In general, I think I should be doing this in packages.json, but the nth-check is not in there.&lt;/p&gt;
&lt;p&gt;After fruitlessly googling around and asking in a couple of discords, I check with ChatGPT to see what she thought.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://blog.iankulin.com/images/screen-shot-2023-01-24-at-10.14.51-am.png"&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2023-01-24-at-10.14.51-am.png" width="828" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Well, I&amp;rsquo;d done all that, and a couple of humans had already told me not to jigger with &lt;code&gt;package-lock.json&lt;/code&gt;, so that was my next stop - I edited it to add the caret and tried &lt;code&gt;npm install&lt;/code&gt; - it just changes it back, which make sense.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s another install script &lt;code&gt;npm ci&lt;/code&gt; which is supposed to use the &lt;code&gt;package-lock.json&lt;/code&gt; rather than doing it&amp;rsquo;s own check, that didn&amp;rsquo;t help either.&lt;/p&gt;
&lt;p&gt;I went back to googling, focusing on the react-scripts (which is basically responsible for building the template React app) and found &lt;a href="https://github.com/facebook/create-react-app/issues/11174"&gt;this issue&lt;/a&gt; on github.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/facebook/create-react-app/issues/11174"&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2023-01-24-at-11.19.20-am.jpg" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Basically, it&amp;rsquo;s claimed to be a problem in how &lt;code&gt;npm audit&lt;/code&gt; works, and can&amp;rsquo;t be a real vulnerability since it&amp;rsquo;s just a tool being used during dev. That still doesn&amp;rsquo;t answer why they don&amp;rsquo;t just update their code to use the newer version on &lt;code&gt;nth-check&lt;/code&gt;, but in any case, it can be safely ignored.&lt;/p&gt;
&lt;p&gt;For people using CI tools that depend on an error free build, the react-scripts can be moved to a different section of the &lt;code&gt;packages.json&lt;/code&gt; file and an argument passed to npm audit to ignore those dependencies.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2023-01-24-at-11.15.54-am.jpg" alt=""&gt;&lt;/p&gt;
&lt;p&gt;So, what have I learned from this? I should have done more looking for answers for the exact error, instead of logically coming up with solutions and then searching for and pursuing them. But also, I have a clear idea of what &lt;code&gt;packages.json&lt;/code&gt; and &lt;code&gt;package-lock.json&lt;/code&gt; do now!&lt;/p&gt;</description></item><item><title>React code is not HTML</title><link>https://blog.iankulin.com/react-code-is-not-html/</link><pubDate>Sun, 22 Jan 2023 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/react-code-is-not-html/</guid><description>&lt;p&gt;I was looking at this ugly code in a React app:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;div style={{overflow: &amp;#39;scroll&amp;#39;, border: &amp;#39;1px solid black&amp;#39;, height: &amp;#39;600px&amp;#39; }}&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; { props.children }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;/div&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Since I don&amp;rsquo;t need any of those CSS properties to change at any stage, I could just convert it to pure HTML/CSS right? Well no:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://blog.iankulin.com/images/screen-shot-2023-01-09-at-4.26.54-pm.png"&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2023-01-09-at-4.26.54-pm.png" width="826" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The newbie trap I&amp;rsquo;ve fallen for here is that although that &lt;code&gt;&amp;lt;div style= tag&lt;/code&gt; looks like HTML, it&amp;rsquo;s actually not. It&amp;rsquo;s not a template that will be filled out in the build step, it&amp;rsquo;s React code that will be used to mutate the virtual DOM.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s not clear to me why React even bothers with this faux-HTML. If it just committed to having developers work with some sort of set of React DOM objects in JS, it would eliminate a couple of layers of complexity - although at the cost of having to learn a new thing. Once you&amp;rsquo;ve committed to transpilation, you might as well go the whole way!&lt;/p&gt;
&lt;p&gt;It does make me wonder what React Native does about building a screen from elements, maybe they already have half of what they need to take that step with React. There seems to be about 500 JavaScript frameworks, so it&amp;rsquo;s entirely possible someone has already done what I&amp;rsquo;m thinking about with out any of React&amp;rsquo;s unarguable success.&lt;/p&gt;
&lt;p&gt;The caveat on these thoughts is the same as always, I&amp;rsquo;m at the very start of my journey, and often the reasons for things are revealed as I go!&lt;/p&gt;
&lt;p&gt;Edit: About five hours after writing the post above, I watched the video below. Turn out this disfigured HTML-eese was a &lt;em&gt;selling&lt;/em&gt; point of React at the time. And it seems like my idea of just having a better language to manipulate the DOM might have been tried and abandoned. 🤦‍♀️&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
 &lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/Wm_xI7KntDs?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
 &lt;/div&gt;
</description></item></channel></rss>