<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
    <channel>
        <title><![CDATA[Amit Shah's Blog posts]]></title>
        <description><![CDATA[Welcome to Amit Shah's blog!]]></description>
        <link>https://amwam.me</link>
        <generator>RSS for Node</generator>
        <lastBuildDate>Tue, 21 Apr 2026 21:48:46 GMT</lastBuildDate>
        <atom:link href="https://amwam.me/rss.xml" rel="self" type="application/rss+xml"/>
        <pubDate>Tue, 21 Apr 2026 21:48:46 GMT</pubDate>
        <copyright><![CDATA[All rights reserved 2026]]></copyright>
        <item>
            <title><![CDATA[Backing out of Nix/NixOS on a Mac]]></title>
            <description><![CDATA[<p>My last post was all about <a href="./nix-nixos-on-macos">Using Nix/NixOS for package management on a Mac</a>. After trying to use it for a few months, I&#x27;ve now uninstalled it.</p>
<p>I still really like the concept of configuring my system as code, especially the ability to define per-project environments without relying on containerisation or virtual machines. However, Nix has several flaws.</p>
<p>The biggest issue for me was the steep learning curve. I never fully grasped the Nix language, which made it difficult to make changes, particularly since I wasn&#x27;t writing Nix code on a regular basis. The syntax and structure are unlike most other tools I’ve used, which made it harder to retain.</p>
<p>On top of that, the error messages were often hard to interpret. Small changes often resulted in build failures, which I then had to spend significant time debugging. The process of deciphering and fixing these issues became frustrating.</p>
<p>In the end, I found myself spending more time trying to get Nix to work than actually working on the projects it was supposed to support.</p>
<p>For now, I&#x27;ve decided to try out <a href="https://www.gnu.org/software/stow/">GNU Stow</a> for managing my dotfiles, which you can see here: <a href="https://github.com/Amwam/Dotfiles">https://github.com/Amwam/Dotfiles</a>. I need to take some time and fully flesh out the configurations, but its a start. A few hours in and its already been easier to manager.</p>
<p>I would like to find a nice way of configuring a new MacOS system via code, but so far I&#x27;ve not been able to find much. I suspect I&#x27;ll dive into some shell scripts to help configure the system the way I like.</p>]]></description>
            <link>https://amwam.me/blog/backing-out-of-nix-nixos-on-a-mac</link>
            <guid isPermaLink="true">https://amwam.me/blog/backing-out-of-nix-nixos-on-a-mac</guid>
            <pubDate>Sat, 07 Jun 2025 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[Using Nix/NixOS for package management on a Mac]]></title>
            <description><![CDATA[<p>Since <a href="./i-bought-a-mac-mini">buying a new Mac</a>, I wanted to try out a new package management solution. I’ve used Homebrew for as long as I can remember.</p>
<h2>What is Nix?</h2>
<p>Nix is a powerful, purely functional package manager that allows you to define and manage software environments declaratively. What sets it apart from traditional package managers is that it provides reproducible builds: no more “works on my machine” scenarios. With Nix, your environments are guaranteed to be exactly the same on every system, ensuring stability and consistency across different machines.</p>
<p>It shares many benefits with Docker, but one key advantage of Nix is its ability to configure a system declaratively, whereas Docker is more focused on packaging and containerizing applications.</p>
<p>Some of the benefits I’m most interested in are:</p>
<ol>
<li>
<p><strong>Declarative Configuration</strong><br/>
Being able to have my desired packages listed out in a file feels a lot more ergonomic to me than hunting through the output of Homebrew.</p>
</li>
<li>
<p><strong>Isolation</strong><br/>
I’m able to set up a set of packages (<a href="https://nixos.wiki/wiki/Development_environment_with_nix-shell">using a shell.nix</a>) on a per-project basis. No more need for version managers installed globally. It’s made my shell startup time a <em>lot</em> quicker.</p>
</li>
</ol>
<p>Here’s an example of a basic <code>shell.nix</code> configuration for a Python project that sets up a specific version of Python, installs some dependencies, and creates an isolated environment:</p>
<pre><code class="language-nix"># shell.nix

{ pkgs ? import &lt;nixpkgs&gt; {} }:

pkgs.mkShell {
  buildInputs = [
    pkgs.python39
    pkgs.python39Packages.pip
    pkgs.python39Packages.virtualenv
  ];

  shellHook = &#x27;&#x27;
    export PIPENV_VENV_IN_PROJECT=1
    echo &quot;Welcome to the isolated Python environment!&quot;
  &#x27;&#x27;;
}
</code></pre>
<p>In this example:</p>
<ul>
<li><code>pkgs.python39</code> ensures the Python version is locked to Python 3.9.</li>
<li><code>pkgs.python39Packages.pip</code> and <code>pkgs.python39Packages.virtualenv</code> make sure <code>pip</code> and <code>virtualenv</code> are available for managing Python packages.</li>
<li>The <code>shellHook</code> section can be used to customize the environment, like setting environment variables or displaying a welcome message when the shell is started.</li>
</ul>
<p>With this configuration, each time I run <code>nix-shell</code> in the project directory, I’ll be placed into a reproducible Python environment, with the same versions and dependencies every time.</p>
<p>Being able to recover my system by rolling back is also interesting, but I’m not really sure how I would use that. Modifying a single config file and reapplying it seems to serve the same outcome for me.</p>
<p>I’ve found the Nix language a bit difficult to grasp, and the documentation isn’t easy to understand for beginners. However, I did manage to get a working setup, which works well for me, and that’s probably good enough for now. I haven’t fully explored creating my own flakes, but that would be useful in the future to be able to share some configuration between my personal and work machines.</p>
<p>Most of my configuration has been done with <a href="https://github.com/LnL7/nix-darwin">nix-darwin</a> and <a href="https://github.com/nix-community/home-manager">Home Manager</a>.</p>
<h2>Nix-Darwin: Tailoring macOS to Your Needs</h2>
<p>While Nix works well on Linux, <strong>Nix-Darwin</strong> is an extension of Nix tailored specifically for macOS. It allows you to manage macOS system settings declaratively, including configuring services, user settings, and even the system’s shell environment. Instead of dealing with complex system preferences manually, Nix-Darwin lets me define everything in one configuration file, making it easy to maintain consistency across devices or recover from a system failure.</p>
<h2>Home Manager: Personalizing Your Development Environment</h2>
<p><strong>Home Manager</strong> is a tool that integrates seamlessly with Nix to manage your user-specific configuration files and software. Think of it as a way to manage your dotfiles, shells, editors, and other personal settings in a reproducible way. For me, managing my dev tools like Neovim, Zsh, and Git configurations with Home Manager has been a game-changer. I can easily sync my configuration across multiple machines and roll back changes if something goes wrong. No more wrestling with hidden configuration files or trying to remember all the tweaks I’ve made to my setup.</p>
<hr/>
<p>I’ve still got a long way to go understanding Nix, but I’ve started configuring my work machine to use a <code>shell.nix</code> for different projects, which has made switching between different language versions and tools a lot easier. When working with unfamiliar projects that haven’t already been converted to Docker, it’s made setting up a container to run the application a lot simpler.</p>]]></description>
            <link>https://amwam.me/blog/nix-nixos-on-macos</link>
            <guid isPermaLink="true">https://amwam.me/blog/nix-nixos-on-macos</guid>
            <pubDate>Sat, 08 Feb 2025 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[Setting up TailScale and VNC]]></title>
            <description><![CDATA[<p>The <a href="./i-bought-a-mac-mini">Mac Mini</a> arrived today!</p>
<p>Have only done some basic setup so far, mostly logging into accounts.</p>
<p>Jumped straight in today, and installed <a href="https://tailscale.com/">TailScale</a>, so now I can access the mac from anywhere, using a VPN. I can also use the mac as an exit node if I want.</p>
<p>Also setup a VNC, so I can use my iPad when away (and also connected via tailscale). Seems like one of the most recomended solutions was <a href="https://edovia.com/en/screens/">Screens</a>. Easy to setup and get started, and works quite well.</p>
<p>Only been able to use it on my local network so far, but performance seems pretty good.</p>
<p>Not a lot to share about the install experience yet, both these two applications didn&#x27;t take up to get working.</p>
<p>Next thing I&#x27;ll likely start looking at is Nix, to install packages, I&#x27;m currently writing this via the GitHub online editor.</p>]]></description>
            <link>https://amwam.me/blog/setting-up-tailscale-and-vnc</link>
            <guid isPermaLink="true">https://amwam.me/blog/setting-up-tailscale-and-vnc</guid>
            <pubDate>Mon, 13 Jan 2025 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[I bought a Mac Mini]]></title>
            <description><![CDATA[<p>I ordered a Mac Mini.
The latest M4 Mac Mini seems like a great deal. Amazing performance, and a (relatively) cheap price.
I&#x27;ve already got a monitor/keyboard/trackpad setup which I use for my main job, and an iPad I use when I travel, so this should fit into my setup quite nicely.
I&#x27;ve gone for a M4, 32GB, 1TB Mac mini. I&#x27;ve opted not to go for the Pro, mainly as I don&#x27;t feel like i need the extra performance.</p>
<h2>Why a new Mac?</h2>
<p>I&#x27;ve been trying to use an iPad as my main machine for some time now. It works well for day-to-day web browsing and email, but i&#x27;ve found it difficult to really do any sort of development with with. I&#x27;ve tried making use of <a href="https://github.com/features/codespaces">GitHub Codespaces</a> and running my own virtual machines. These have tended to work well, with the biggest issue being iPad OS. Its mouse and keyboard support make it difficult to be <em>truely</em> productive on it.</p>
<p>I&#x27;ve also wanted to start exploring some software engineering items outside of my regular job, things that may not fit with what I do regularly. Having my own machine I can do that on is a major plus point.</p>
<p>There are also other admin activities I have to take care of, that I think would be better suited to a desktop computer. Using the iPad plugged in with Stage Manager <em>works</em>, but isn&#x27;t often ideal for my workflow.</p>
<h2>What do I want to do?</h2>
<p>I&#x27;ve got a bunch of ideas of things I want to explore, so here is a non-exhaustive list. I may not get around to all of these, but I itend to write up  journey.</p>
<h3>Dev tools</h3>
<ul>
<li>Switching from <code>pyenv</code>/<code>virtualenv</code> to <a href="https://docs.astral.sh/uv/"><code>uv</code></a>.
<ul>
<li>I write a lot of Python code, <code>uv</code> seems to a favourite in the community. I&#x27;ve been using pyenv/virtualenv for over 10 years now, so now feels like a good time to start exploring other alteratives</li>
</ul>
</li>
<li>Shell environment.
<ul>
<li>I&#x27;ve been using <code>zsh</code> with <a href="https://ohmyz.sh/"><code>oh-my-zsh</code></a> for the majority of my career, and have never attempted to look into alternatives. I&#x27;m not sure if i&#x27;m really making use of the functionality of <code>oh-my-zsh</code></li>
</ul>
</li>
<li><a href="https://nixos.org/">Nix/NixOS</a> for declarative environments
<ul>
<li>Reproducable environments has always been a positive in my experience.</li>
</ul>
</li>
<li>Vim/Neovim
<ul>
<li><a href="https://amwam.me/blog/my-vimrc">I setup a neovim configuration quite some time ago</a>, and haven&#x27;t changed it since. I used to work on a mix of Python 2 and 3 projects, so had a lot of configuration to deal with that. I suspect that theres probably more modern ways of doing the things I had configured. There was also a lot of plugins I added, which I&#x27;m not sure are really needed/used. I&#x27;m also not sure on the status of Vim vs Neovim, so worth looking into that too.</li>
</ul>
</li>
<li>Docker for Mac alternatives
<ul>
<li>Haven&#x27;t looked at other options for a while, but there are a few out there. I&#x27;d like to run some services on the new machine, and using containers feels like the easiest way.</li>
</ul>
</li>
</ul>
<h2>Other Stuff</h2>
<ul>
<li>AI
<ul>
<li>Just in general, I want to start experimenting a bit more, having a decenent machine I could run some LLMs locally will be useful</li>
</ul>
</li>
<li>iOS/iPadOS/MacOS development
<ul>
<li>I used to do a lot of this many years ago, things have changed so much, I&#x27;d like to get back into it. I&#x27;ve got a few ideas for apps I&#x27;d love to try and build.</li>
</ul>
</li>
<li><a href="https://tailscale.com/">TailScale</a>
<ul>
<li>I already have this setup so that I can connect to my home network and use my AppleTV as an exit node. I&#x27;d also like to try and setup VSCode server, so that I could use my iPad on the go, connecting to the mac. Will also want to look at VNC options.</li>
<li>Also interested in setting up <a href="https://pi-hole.net/">PiHole</a> again for DNS blocking, and running that from the Mac.</li>
</ul>
</li>
<li>Home Automation
<ul>
<li>I used to have a lot of smart switches around, but haven&#x27;t really got them setup yet (since moving).</li>
</ul>
</li>
</ul>]]></description>
            <link>https://amwam.me/blog/i-bought-a-mac-mini</link>
            <guid isPermaLink="true">https://amwam.me/blog/i-bought-a-mac-mini</guid>
            <pubDate>Mon, 30 Dec 2024 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[Preventing double clicks in React, with Hooks]]></title>
            <description><![CDATA[<p>This is a follow up to <a href="https://amwam.me/blog/preventing-double-clicks-in-react">my previous post</a>, about preventing double clicks in React, this time using <a href="https://react.dev/reference/react/hooks">React Hooks</a>.</p>
<p>When building interactive web applications, it&#x27;s crucial to prevent users from accidentally submitting forms or triggering actions multiple times. One effective and user-friendly approach is to disable the button immediately after it&#x27;s clicked.</p>
<h2>The Problem</h2>
<p>Consider a form submission button:</p>
<pre><code class="language-javascript">function SubmitButton() {
  const handleSubmit = () =&gt; {
    // Perform some action, e.g., API call
    console.log(&quot;Form submitted!&quot;);
  };

  return &lt;button onClick={handleSubmit}&gt;Submit&lt;/button&gt;;
}
</code></pre>
<p>If a user clicks this button twice quickly, the <code>handleSubmit</code> function will be called twice, potentially causing duplicate form submissions or API calls.</p>
<p>We can prevent double clicks by disabling the button immediately after it&#x27;s clicked. Here&#x27;s how we can implement this using React hooks:</p>
<pre><code class="language-javascript">import React from &quot;react&quot;;

function submitForm() {
  // Simulating an async action that returns a Promise
  return new Promise((resolve) =&gt; setTimeout(resolve, 1000));
}

function SubmitButton() {
  const [isSubmitting, setIsSubmitting] = React.useState(false);

  const handleSubmit = async () =&gt; {
    if (isSubmitting) {
      return; // Prevent double submission
    }

    setIsSubmitting(true);

    try {
      // Perform some action, e.g., API call
      await submitForm();
      console.log(&quot;Form submitted successfully!&quot;);
    } catch (error) {
      console.error(&quot;Error submitting form:&quot;, error);
    } finally {
      setIsSubmitting(false);
    }
  };

  return (
    &lt;button onClick={handleSubmit} disabled={isSubmitting}&gt;
      {isSubmitting ? &quot;Submitting...&quot; : &quot;Submit&quot;}
    &lt;/button&gt;
  );
}
</code></pre>
<p>Let&#x27;s break down this implementation:</p>
<ol>
<li>We use the <code>useState</code> hook to create an <code>isSubmitting</code> state variable, this lets us track when some background action is being run.</li>
<li>The <code>handleSubmit</code> function:
<ul>
<li>Checks if the form is already submitting and returns early if it is.</li>
<li>Sets <code>isSubmitting</code> to <code>true</code> before starting the submission process.</li>
<li>Uses a try/catch block to handle the submission and any potential errors.</li>
<li>Sets <code>isSubmitting</code> back to <code>false</code> in the <code>finally</code> block, ensuring the button is re-enabled even if an error occurs.</li>
</ul>
</li>
<li>The button&#x27;s <code>disabled</code> prop is tied to the <code>isSubmitting</code> state, so that the user can&#x27;t click it again to trigger the action.</li>
<li>The button text changes to provide feedback to the user.</li>
</ol>
<p>We can also take this a step further, and create our own hook to extract this logic.</p>
<pre><code class="language-javascript">import React from &#x27;react&#x27;;

function submitForm() {
    // Simulating an async action that returns a Promise
    return new Promise(resolve =&gt; setTimeout(resolve, 1000))
}

export function useIsSubmitting(action): [loading, action] {
  const [loading, setLoading] = React.useState(false);

  function doAction(...x) {
    setLoading(true);
    return action(...x).finally(() =&gt; setLoading(false));
  }

  return [loading, doAction];
}

function SubmitButton() {
  const [isSubmitting, doHandleSubmit] = useIsSubmitting(submitForm);

  return (
    &lt;button onClick={doHandleSubmit} disabled={isSubmitting}&gt;
      {isSubmitting ? &quot;Submitting...&quot; : &quot;Submit&quot;}
    &lt;/button&gt;
  );
}


</code></pre>
<p>The final component doesn&#x27;t need to handle any logic for reseting <code>isSubmitting</code>, and can focus on rendering the different states.</p>
<h2>Conclusion</h2>
<p>Disabling the button after it&#x27;s clicked is a simple yet effective way to prevent double clicks and double submissions in React applications. This approach provides clear visual feedback to the user and ensures that actions are only triggered once, even if the user accidentally clicks multiple times. By combining this with proper error handling and timeouts, you can create a robust and user-friendly interface for your forms and action buttons.</p>]]></description>
            <link>https://amwam.me/blog/preventing-double-clicks-in-react-with-hooks</link>
            <guid isPermaLink="true">https://amwam.me/blog/preventing-double-clicks-in-react-with-hooks</guid>
            <pubDate>Mon, 05 Aug 2024 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[Optimising Docker builds with multi-stage targers]]></title>
            <description><![CDATA[<p>I&#x27;ve found that one of the most powerful features in Docker is the ability to use multi-stage builds. This can significantly improve your development workflow and optimize your production containers, by allowing extra dependencies locally, without bloating the final production image.</p>
<h2>Understanding Multi-Stage Builds</h2>
<p>Multi-stage builds allow you to use multiple <code>FROM</code> statements in your Dockerfile. Each <code>FROM</code> instruction can use a different base, and each begins a new stage of the build. You can selectively copy artifacts from one stage to another, leaving behind everything you don&#x27;t want in the final image. You can also build specific <em>parts</em> of the Docker file, by specifying which target you wish to build. With this you can have a single Dockerfile for both development and production, without needing to duplicate their contents.</p>
<h2>Separating Dev and Prod Dependencies</h2>
<p>One of the key benefits of multi-stage builds is the ability to separate your development and production dependencies. This is particularly useful in Python projects where you often have different requirements for development (like testing and linting tools) and production.</p>
<p>Let&#x27;s look at an example Dockerfile:</p>
<pre><code class="language-dockerfile"># Stage 1: Base image for both dev and prod
FROM python:3.12 AS base

RUN pip install requests

# Stage 2: Production build
FROM base AS prod
ENV IS_PROD=true

# Stage 3: Development dependencies
FROM base AS dev
RUN pip install pytest

ENV IS_PROD=false
</code></pre>
<p>In this Dockerfile, we have three stages:</p>
<ol>
<li><code>base</code>: A common base stage with Python 3.12.</li>
<li><code>prod</code>: Includes only production dependencies.</li>
<li><code>dev</code>: Includes all dependencies (including development ones).</li>
</ol>
<h2>Building Specific Targets</h2>
<p>To build a specific target, you can use the <code>--target</code> flag:</p>
<pre><code class="language-bash"># Build the development image
docker build --target dev -t myapp:dev .

# Build the production image
docker build --target prod -t myapp:prod .
</code></pre>
<h2>Default Build Behavior</h2>
<p>It&#x27;s important to note that if you don&#x27;t specify a target, Docker will build the last stage by default. In our example, if you run <code>docker build .</code> without any flags, it will build the <code>dev</code> stage.</p>
<h2>Benefits of Multi-Stage Builds</h2>
<p>Let&#x27;s dive deeper into why using multi-stage builds is beneficial:</p>
<ol>
<li>
<p><strong>Smaller Production Images</strong>:</p>
<ul>
<li>By separating dev and prod dependencies, your production image only contains what&#x27;s necessary to run the application, resulting in a smaller image size.</li>
<li>A typical Python web application might require development tools like <code>pytest</code>, <code>flake8</code>, and <code>mypy</code>, which are not needed in production. By excluding these, you can reduce your image size.</li>
<li>This can be beneficial for services like AWS Fargate and AWS Lambda, as it reduces the load time for the services to download the container image.</li>
</ul>
</li>
<li>
<p><strong>Improved Security</strong>:</p>
<ul>
<li>Fewer dependencies in your production image means a reduced attack surface.</li>
<li>Example: Development tools often have their own dependencies and potential vulnerabilities. By excluding them from your production image, you minimize the risk of these vulnerabilities being exploited.</li>
<li>You can also use multi-stage builds to run security scans on your code before creating the final production image, ensuring that only vetted code makes it to production.</li>
</ul>
</li>
<li>
<p><strong>Faster Builds and Deployments</strong>:</p>
<ul>
<li>Smaller images are quicker to build, push, and pull, speeding up your CI/CD pipelines.</li>
<li>In a microservices architecture with dozens of services, reducing each container size by 100MB can save gigabytes of data transfer and storage, significantly speeding up deployments.</li>
<li>Faster builds mean quicker feedback loops for developers.</li>
</ul>
</li>
<li>
<p><strong>Consistency</strong>:</p>
<ul>
<li>Using the same base image for both development and production ensures consistency across environments.</li>
<li>This reduces &quot;it works on my machine&quot; problems by ensuring that the development environment closely mirrors production.</li>
</ul>
</li>
<li>
<p><strong>Flexibility</strong>:</p>
<ul>
<li>You can easily switch between development and production builds without maintaining separate Dockerfiles.</li>
<li>This simplifies your build process and reduces the chance of discrepancies between environments.</li>
</ul>
</li>
<li>
<p><strong>Optimized Build Cache</strong>:</p>
<ul>
<li>Multi-stage builds allow you to optimize your build cache more effectively.</li>
<li>You can structure your Dockerfile so that layers that change less frequently (like installing dependencies) are earlier in the file, while layers that change more often (like copying your application code) are later.</li>
<li>This means that subsequent builds can reuse cached layers more effectively, speeding up your build process.</li>
</ul>
</li>
<li>
<p><strong>Easy Integration of Build Tools</strong>:</p>
<ul>
<li>You can use specialized build tools or compilers in early stages without bloating your final image.</li>
</ul>
</li>
</ol>
<h2>Drawbacks</h2>
<p>One of the biggest drawbacks using multi-stage builds, is the inability to re-use sections in the middle of a stage.</p>
<p>For instance, typically I would want to have <code>dev</code> and <code>prod</code> images separated only by the dependencies they install. However to produce an optimise build, you would want to add you application code <em>after</em> building the dependencies. This would therefore mean you would have to duplicate the <code>COPY</code> commands in both the <code>dev</code> and <code>prod</code> stages.</p>
<p>Multi-stage builds in Docker offer a powerful way to optimize your container images and streamline your development workflow. By separating your dev and prod dependencies, you can ensure that your production containers are lean, secure, and efficient, while still maintaining a robust development environment.</p>]]></description>
            <link>https://amwam.me/blog/docker-multi-stage-targets</link>
            <guid isPermaLink="true">https://amwam.me/blog/docker-multi-stage-targets</guid>
            <pubDate>Thu, 04 Jul 2024 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[React in python (Pyodide)]]></title>
            <description><![CDATA[<p><a href="https://github.com/iodide-project/pyodide">Pyodide</a> is a new project from Mozilla.
It aims to bring python into the browser, making use of WebAssembly. You can read the full blog post at <a href="https://hacks.mozilla.org/2019/04/pyodide-bringing-the-scientific-python-stack-to-the-browser/">https://hacks.mozilla.org/2019/04/pyodide-bringing-the-scientific-python-stack-to-the-browser/</a>.</p>
<p>I wanted to try it out, and see how easy it would be run python in the browser, while also being able to interact with the DOM, as I would normally do in javascript. Turns out, Pyodide makes this really simple. Pyodide will proxy javascript objects with their python counter parts, in a way that feels very seamless.</p>
<p>After I had got the project setup, I decided to have a go and running a small React application, written in Python.
As React isn&#x27;t actually dependant on JSX syntax, this is trivial to get running thanks to Pyodide. All React components just have to return <strong>React.createElement</strong>` calls instead (which babel would normally translate JSX code into).</p>
<p>The result:</p>
<pre><code class="language-python">from js import document, window

React = window.React
ReactDOM = window.ReactDOM


def HelloWorldReactComponent():
    return React.createElement(
        &quot;div&quot;, {&quot;style&quot;: {&quot;display&quot;: &quot;flex&quot;}},
        &quot;Hello world. This was rendered with React.&quot;
    )


ReactDOM.render(HelloWorldReactComponent(), document.querySelector(&quot;#result&quot;))
</code></pre>
<p>You can see a live demo at: <a href="https://amwam-pyodide-testing.now.sh">https://amwam-pyodide-testing.now.sh</a> or alternatively the code at: <a href="https://github.com/Amwam/pyodide-testing-playground">https://github.com/Amwam/pyodide-testing-playground</a></p>]]></description>
            <link>https://amwam.me/blog/react-in-python</link>
            <guid isPermaLink="true">https://amwam.me/blog/react-in-python</guid>
            <pubDate>Mon, 22 Apr 2019 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[Preventing double clicks in React]]></title>
            <description><![CDATA[<h2><em>UPDATE 5th August 2024</em>: An updated version of this post, using React Hooks is available at: <a href="https://amwam.me/blog/preventing-double-clicks-in-react-with-hooks">https://amwam.me/blog/preventing-double-clicks-in-react-with-hooks</a></h2>
<p>Most of the time in React you will pass an onClick handler to a button, and whenever that button is click, expect that function to be called.</p>
<pre><code class="language-javascript">&lt;button onClick={() =&gt; console.log(&quot;I was called&quot;)}&gt;Click me&lt;/button&gt;
</code></pre>
<p>This <code>onClick</code> will get called, every time the button is clicked.
Sometimes this function might call an endpoint, and in some cases, you may not want that endpoint to run multiple times.</p>
<p>To get around this, you can use state to detect if the button was clicked, disable the button from being clicked further, then perform our action.
The steps would be:</p>
<ol>
<li>Click the button</li>
<li>Set the state of the component to track the button click</li>
<li>Update the button (re-render) to disable it, and prevent further clicking</li>
<li>Perform our action (using <code>componentDidUpdate</code>)</li>
<li>Restore the button state, once the action is complete</li>
</ol>
<h2>Example:</h2>
<p>Below is a custom component that handles doubleclicking of buttons. All of its props are passed down to the HTML <code>button</code> element, except the <code>onClick</code> handler, which we will wrap with our custom logic.</p>
<pre><code class="language-javascript">import React from &quot;react&quot;;

class PreventDoubleClickButton extends React.component {
  constructor(props) {
    super(props);
    this.state = { clicked: false };

    this.onClick = this.onClick.bind(this);
  }

  onClick(event) {
    this.setState({ clicked: true });
  }

  componentDidUpdate(prevProps, prevState) {
    // If we previously weren&#x27;t clicked, and then updated to be clicked, we want to run our click handler;
    if (!prevState.clicked &amp;&amp; this.state.clicked) {
      // Here we expect the onClick function passed down, to return a Promise, once complete
      this.props.onClick().then(() =&gt; {
        this.setState({ clicked: true });
      });
    }
  }

  render() {
    return (
      &lt;button {...this.props} onClick={this.onClick}&gt;
        {this.props.children}
      &lt;/button&gt;
    );
  }
}

// Usage

function handleClick() {
  return fetch(&quot;./myData.json&quot;).then(() =&gt; {
    this.setState({ data: &quot;Got it!&quot; });
  });
}

&lt;PreventDoubleClickButton className=&quot;myButton&quot; onClick={this.handleClick}&gt;
  Click me
&lt;/PreventDoubleClickButton&gt;;
</code></pre>]]></description>
            <link>https://amwam.me/blog/preventing-double-clicks-in-react</link>
            <guid isPermaLink="true">https://amwam.me/blog/preventing-double-clicks-in-react</guid>
            <pubDate>Mon, 14 Jan 2019 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[Switching from flow to TypeScript]]></title>
            <description><![CDATA[<p>Recently I&#x27;ve made the switch in several projects from using <a href="https://flow.org">Flow</a> to <a href="https://www.typescriptlang.org">TypeScript</a>, and I thought I would write down a few of the reasons why.</p>
<h2>1. Flow was hard to keep up to date</h2>
<p>In one project we were on flow version 0.50 (at the time of writing 0.87 is out), every time we tried to upgrade we suffered broken changes.
This got worse and worse as time went on, meaning we were less and less likely to upgrade.
This is partly our own fault for not keeping our dependencies up to date, but also partly due to the fact flow isn&#x27;t a v1 tool, and that we shouldn&#x27;t have relied on it being such.</p>
<h2>2. Using Flow felt like a chore</h2>
<p>I saw over time, developers would treat flow like they treat a linter, only correcting things when there was an error in build, rather than constructing types as they went along.
This suggested that my team weren&#x27;t in favour of using flow (or maybe even a type system at all)<a href="#f1">[1]</a>.
If you only run type checks, after you have a feature working, it becomes a pain to go back and correct errors, even if they may be legitimate errors. This then encourages poor type checking or laziness.</p>
<h2>3. Poor community support</h2>
<p>Quite often it was difficult to get accurate types for third party libraries, and we regularly had to write our own, which then led to further problems with accuracy.
TypeScript&#x27;s community involvement seems to be a lot higher, and so far I&#x27;ve not faced any issues with type information with external libraries, finding that may other libraries are even written in TypeScript.</p>
<h2>4. Strong type checking</h2>
<p>This may not be entirely accurate phrasing.
However, as TypeScript code can&#x27;t run unless it compiles successfully it encourages writing correct types as you go, rather than after completing work.
This results in fewer errors, and actually <em>less</em> time spent debugging, than if the code is written in Flow/plain JS (this is purely anecdotal).</p>
<p>These are just a few of the reasons as to why I&#x27;ve switched over a few of my projects to TypeScript recently, and is certain not an exhaustive list.</p>
<p><strong>1</strong> Turns out my team <em>did</em> like typechecking, but would not run it alongside their editing, so would often forget. <a href="#a1">↩</a></p>]]></description>
            <link>https://amwam.me/blog/flow-to-typesript</link>
            <guid isPermaLink="true">https://amwam.me/blog/flow-to-typesript</guid>
            <pubDate>Sun, 02 Dec 2018 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[Using React with Web Components]]></title>
            <description><![CDATA[<p>Dealing with legacy code can be tough, even more so if you want to slowly migrate your front end to React.</p>
<p>Recently I found myself in a situation where some HTML was rendered server side, and inserted into the browser on request. I wanted to use a shiny new React component, instead of falling back to the legacy jQuery code.</p>
<p>In comes <a href="https://www.webcomponents.org">Web Components</a>!</p>
<p>Web Components allow you to write your own HTML tags, and use them through out your application. This solves my problem, by having the server render the new HTML tag, and the front end automatically load the required JavaScript to render/control it. When rendering the Web Component we can delegate the actual rendering to React.</p>
<p>Lets start with a simple React component:</p>
<pre><code class="language-JavaScript">import React from &quot;react&quot;;

class MyComponent extends React.Component {
  render() {
    return &lt;div&gt;Hello world! This was rendered using React!&lt;/div&gt;;
  }
}
</code></pre>
<p>In order to render the component, we have to create a new HTMLElement as so:</p>
<pre><code class="language-JavaScript">class CustomHTMLElement extends HTMLElement {
  connectedCallback() {
    ReactDOM.render(&lt;MyComponent /&gt;, this);
  }
}
</code></pre>
<p>In this, when the component is loaded by the browser, the <code>connectedCallback</code> will be called, allowing us to render the new component, at <code>this</code> (the location of the element in the DOM).</p>
<p>To finish, we just have to register the element with the customElements API:</p>
<pre><code class="language-JavaScript">customElements.define(&quot;x-test&quot;, CustomHTMLElement);
</code></pre>
<p>Now we can just use the tag <code>&lt;x-test/&gt;</code> in our HTML, to render the React component!</p>
<p>One problem with this setup can be <a href="https://babeljs.io">Babel</a>, as it doesn&#x27;t play nice with transforming classes and extending <code>HTMLElement</code>.
We can work around this using a custom class, and extending the prototype of HTMLElement:</p>
<pre><code class="language-JavaScript">function BabelHTMLElement() {
  const newTarget = Object.getPrototypeOf(this).constructor;
  return Reflect.construct(HTMLElement, [], newTarget);
}

Object.setPrototypeOf(BabelHTMLElement.prototype, HTMLElement.prototype);

class CustomHTMLElement extends BabelHTMLElement {
    ...
</code></pre>
<p>Now our <code>CustomHTMLElement</code> extends the <code>BabelHTMLElement</code> rather the <code>HTMLElement</code> directly.</p>
<p>To view an example project with this in use, you can checkout the repo here: <a href="https://github.com/Amwam/react-webcomponents">https://github.com/Amwam/react-webcomponents</a></p>]]></description>
            <link>https://amwam.me/blog/react-web-components</link>
            <guid isPermaLink="true">https://amwam.me/blog/react-web-components</guid>
            <pubDate>Sun, 02 Jul 2017 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[Sorting Javascript imports]]></title>
            <description><![CDATA[<p>I&#x27;ve published my first NPM package <a href="https://www.npmjs.com/package/js-import-sort">js-import-sort</a>!
Its a JS tool to sort and organise ES2015/ES6 imports. You can find it on <a href="https://github.com/Amwam/js-import-sort">GitHub</a>.</p>
<p>It was created using a tool from facebook called <a href="https://github.com/facebook/jscodeshift">jscodeshift</a>, which is a library for building javascript codemods.</p>
<p>Example:</p>
<pre><code>import Main from &#x27;aaaa&#x27;;
import {ZMain}  from &#x27;aaaa&#x27;;
   

import First from &#x27;zzz&#x27;;
import {Third} from &#x27;zzz&#x27;;


import {Second} from &#x27;zzz&#x27;;


import * as someDefault from &#x27;bbb&#x27;;


import {a as b} from &#x27;packageModule&#x27;;


import SomeClass from &#x27;./MyModule&#x27;;
import AnotherClass from &#x27;../../Module1&#x27;;


import * as util from &#x27;util&#x27;;
</code></pre>
<p>Becomes:</p>
<pre><code>import * as util from &#x27;util&#x27;;

import Main, {ZMain} from &#x27;aaaa&#x27;;
import * as someDefault from &#x27;bbb&#x27;;
import First, {Second, Third} from &#x27;zzz&#x27;;

import {a as b} from &#x27;packageModule&#x27;;

import AnotherClass from &#x27;../../Module1&#x27;;
import SomeClass from &#x27;./MyModule&#x27;;
</code></pre>
<p>Imports are separated by node, dependencies in <code>package.json</code>, other and relative imports.</p>]]></description>
            <link>https://amwam.me/blog/js-import-sort</link>
            <guid isPermaLink="true">https://amwam.me/blog/js-import-sort</guid>
            <pubDate>Sun, 17 Jul 2016 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[Update Jenkins workflow to 1.3]]></title>
            <description><![CDATA[<p>I&#x27;ve updated my <a href="http://www.alfredapp.com/">Alfred</a> workflow for searching through jobs on Jenkins. You can find it on <a href="http://www.packal.org/workflow/jenkins">Packal</a> or on <a href="https://github.com/Amwam/Jenkins-Alfred-Workflow/releases">GitHub Releases</a>.</p>
<p>This release contains an update to the login flow to make use of the API token, allowing the workflow to work with any authentication strategy.</p>
<p>There is also now support for nested job structures (such as using the folders plugin)!</p>]]></description>
            <link>https://amwam.me/blog/update-jenkins-workkflow--1-3</link>
            <guid isPermaLink="true">https://amwam.me/blog/update-jenkins-workkflow--1-3</guid>
            <pubDate>Sun, 15 May 2016 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[Maintaining a clean Git history]]></title>
            <description><![CDATA[<p>Ensuring the git history of your project (or any version controlled project) can be an incredibly useful asset.
In particular for new developers, trying to understand why code has been written in a certain way.
With clear and ordered commit messages, it can become obvious as to why a change was made.</p>
<p>There are two practices I apply to make sure that I leave my code changes as clear as I can.
They are how I write the commit messages, and the
ordering of the actual commits.</p>
<h3>Commit messages</h3>
<p>The message in a commit is where an explanation for the change should reside.
This should include at the very least what was changed, and why.</p>
<p>Some examples of a bad commit messages include (and yes, I have seen these kind of commits from multiple developers on different projects):</p>
<blockquote>
<p>fixed bug</p>
</blockquote>
<p>and</p>
<blockquote>
<p>refactored stuff</p>
</blockquote>
<p>A more descriptive commit message would look something like</p>
<blockquote>
<p>Fix ImageProcessingFactory not handling images less than 50px wide</p>
</blockquote>
<p>Commit messages should ideally tell you what a commit will do if it is applied.
I try to write my messages as if being read as <code>This commit will &lt;commit message&gt;</code></p>
<p>An even better commit message would have a slightly longer message, after the initial message, describing the problem/change in more detail.
However, this isn&#x27;t always practical or even necessary depending on the project.
If you have a chance I would recommend taking a look at the git and linux kernal commit messages as examples of great ones!</p>
<p>I&#x27;ve also worked in places that would prepend the ticket number to the commit message. Allowing developers to view the reasons for a change in more
detail.</p>
<h3>Autosquashing commits</h3>
<p>Rebasing commit allows you to keep your history linear.
While not everyone agrees whether or not you should be maintaining a linear history,
It is almost definitely (in my opinion) and good idea to keep your local branches ordered appropriately, by showing changes as a series of changes to be applied.
Ideally each change is isolated from the others (within reason), and can be applied separately.</p>
<p>Development rarely works like this. Too often have I made some changes, progressed a bit further, and realised I there were more changes to make in
that original commit.
Maybe it was a left over debug statement, or some missing config, or even a chunk of functionality I didn&#x27;t originally think about.
Typically most people would just make a new commit with these changes, and leave it at that.</p>
<p>Git has the ability to squash commits into another, allowing you to make several small changes, and then bundle them up into one, more appropiate,
commit.
This get a bit complicated, when you start to make changes that aren&#x27;t in order.</p>
<p>Luckily, git has that solved, with <code>--fixup</code> and <code>--squash</code>. These let you commit changes, and at the same time specify that they should be part of a
previous commit.
The <code>fixup</code> option will allow you to <code>amend</code> a previous commit, and not change the original commit  message.
The <code>squash</code> options, will also <code>amend</code> a previous commit, but it will also give you the change to alter the commit message.</p>
<p>To use these, just prepare you staging area, like normal (<code>git add</code> or <code>git add -p</code>), then commit with:</p>
<pre><code>`git commit --fixup=&lt;previous commit hash&gt;`
</code></pre>
<p>This will create a new commit, with a message in the form</p>
<pre><code>!fixup &lt;original commit message&gt;
</code></pre>
<p>Then when you are at a stage you wish to squash and clean all your commits, just run a rebase with the <code>--autosquash</code> flag (or you can set
<code>rebase.autosquash</code> to true in you git config).</p>
<p>You history would then go from:</p>
<blockquote>
<p>commit 1234
        !fixup Original change</p>
</blockquote>
<blockquote>
<p>commit 5678
        Some other change</p>
</blockquote>
<blockquote>
<p>commit 91011
        Original change</p>
</blockquote>
<p>becoming:</p>
<blockquote>
<p>commit 5678
        Some other change</p>
<p>commit 91011
        Original change</p>
</blockquote>
<p>Where commit 91011, contains all the changes from the original <code>91011</code>, and <code>1234</code></p>
<p>Rebasing commits can sometimes be a little scary if you don&#x27;t know what you are doing or how it really works.
So squashing commits may not be for everyone, but I would still recommend everyone to put in more effort to their commit messages, as it can help
everyone involved in modifying a codebase.</p>]]></description>
            <link>https://amwam.me/blog/maintaining-a-clean-git-history</link>
            <guid isPermaLink="true">https://amwam.me/blog/maintaining-a-clean-git-history</guid>
            <pubDate>Sun, 11 Oct 2015 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[Using Git push-to-deploy]]></title>
            <description><![CDATA[<p>I added the ability to push to deploy.</p>
<p>I found out about Git&#x27;s &#x27;push to deploy&#x27; feature today based on this <a href="https://github.com/blog/1957-git-2-3-has-been-released">post</a> and figured its perfect for my setup!
My site is built so that I don&#x27;t have to restart when a new post is added, which means silent updates.</p>
<p>I already had a hook which would check out my repo, and pull the latest changes, whenever I pushed to the bare repo.
This makes things simpler as I can now choose exactly when I want to deploy.</p>
<p>To set this up, all you have to do is clone your repo on your server, as you would normally (<code>git clone http://path.to.your.repo</code>),
then run the following command to allow pushing into the repo.</p>
<pre><code>git config receive.denyCurrentBranch updateInstead
</code></pre>
<p>From your development side, setup a new remote such as <code>prod</code> with the command :</p>
<pre><code>git remote add prod http://path.to.your.production.repo
</code></pre>
<p>When ever you want to deploy it becomes a simple case of just running <code>git push prod master</code>.</p>]]></description>
            <link>https://amwam.me/blog/git-push-to-deploy</link>
            <guid isPermaLink="true">https://amwam.me/blog/git-push-to-deploy</guid>
            <pubDate>Fri, 06 Feb 2015 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[Update Jenkins workflow to 1.0.1]]></title>
            <description><![CDATA[<p>I update my <a href="http://www.alfredapp.com/">Alfred</a> Jenkins workflow to 1.0.1 today (<a href="http://amwam.me/blog/alfred-jenkins-workflow">Original post</a>).</p>
<p>It is just a minor release with a few bug fixes, and mainly a tidy up of the tests, making sure its up to 100% coverage.
Also removed the mockito package, as there really was no need for it.</p>
<p>You can download it from either <a href="http://www.packal.org/workflow/jenkins">Packal</a> or directly from <a href="https://github.com/Amwam/Jenkins-Alfred-Workflow/releases">GitHub Releases</a>.</p>
<p>If you&#x27;re interested in checking out the source code, you can find it here: <a href="https://github.com/Amwam/Jenkins-Alfred-Workflow">https://github.com/Amwam/Jenkins-Alfred-Workflow</a></p>]]></description>
            <link>https://amwam.me/blog/update-jenkins-workflow--1-0-1</link>
            <guid isPermaLink="true">https://amwam.me/blog/update-jenkins-workflow--1-0-1</guid>
            <pubDate>Tue, 20 Jan 2015 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[My .vimrc]]></title>
            <description><![CDATA[<p>Finally got around to putting my .vimrc files into git.
Lately I&#x27;ve been playing with vim more and more, and I&#x27;m really starting to like it.</p>
<p>If you would like to check out my current configs, you can do so here at <a href="https://github.com/Amwam/vimrc">GitHub</a>.</p>]]></description>
            <link>https://amwam.me/blog/my-vimrc</link>
            <guid isPermaLink="true">https://amwam.me/blog/my-vimrc</guid>
            <pubDate>Sun, 11 Jan 2015 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[Alfred: Jenkins workflow]]></title>
            <description><![CDATA[<link rel="preload" as="image" href="/images/alfred-jenkins/all-jobs.png"/><link rel="preload" as="image" href="/images/alfred-jenkins/filtered-jobs.png"/><p>I&#x27;ve created a <a href="http://www.alfredapp.com/">Alfred</a> workflow to help search through jobs on a Jenkins CI instance.</p>
<p>It makes use of the standard Jenkins JSON API, and provides a way to fuzzy search through the job names, and view their current status.</p>
<p><img src="/images/alfred-jenkins/all-jobs.png" alt="All jobs" title="All jobs"/></p>
<p>Here is the view with all jobs being shown, the shortcut for this is <code>jenkins</code> (or <code>cmd</code>+<code>alt</code>+<code>ctrl</code>+<code>j</code>)</p>
<p><img src="/images/alfred-jenkins/filtered-jobs.png" alt="Filtered jobs" title="Filtered jobs"/></p>
<p>Simply start typing to filter down the list of jobs.</p>
<p>You can download the latest from either <a href="http://www.packal.org/workflow/jenkins">Packal</a> or directly from <a href="https://github.com/Amwam/Jenkins-Alfred-Workflow/releases">GitHub Releases</a>.</p>
<p>If you&#x27;re interested in checking out the source code, you can find it here: <a href="https://github.com/Amwam/Jenkins-Alfred-Workflow">https://github.com/Amwam/Jenkins-Alfred-Workflow</a></p>]]></description>
            <link>https://amwam.me/blog/alfred-jenkins-workflow</link>
            <guid isPermaLink="true">https://amwam.me/blog/alfred-jenkins-workflow</guid>
            <pubDate>Wed, 03 Sep 2014 00:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[My First post]]></title>
            <description><![CDATA[<p>This is my <em>first</em> post on this site</p>
<p>For a bit of reference this is a python Flask website.</p>
<p>And I&#x27;m using markdown to write all this!</p>]]></description>
            <link>https://amwam.me/blog/my-first-post</link>
            <guid isPermaLink="true">https://amwam.me/blog/my-first-post</guid>
            <pubDate>Sat, 05 Jul 2014 00:00:00 GMT</pubDate>
        </item>
    </channel>
</rss>