React migration of IR35 Shield Dashboard

Frontend project leader for converting a SaaS product dashboard from Vanilla JS to React with Typescript. Integrated several NodeJS packages and fully unit tested using Jest.

IR35 Shield Dashboard
IR35 Shield Dashboard

This project marked the migration of an existing dashboard built with .NET Razor and Vanilla JS on the frontend, to one that uses React and Typescript. The transition aimed to enhance the user experience and leverage the benefits of a modern frontend framework.

As the IR35 Shield dashboard had grown more complex throughout development, it had become increasingly difficult to manage the scale of the frontend code using Vanilla Javascript alone. Migrating to React would give us the ability to split UI elements into small, self-contained components, keeping the code more manageable as the application scales up in size.

Migrating to React would allow us to create a richer UI that responds to changes in state or data updates without requiring page reloads, providing a more seamless experience for the user. I was tasked with leading the frontend part of this project and given the responsibility of choosing which tools and packages to use; I was also required to substantiate my decision making with valid reasoning.

Approach

The team decided that we would convert a single page as the first step, including the global features that exist on all dashboard pages: the navbar, sidebar, user menus, and global modals. Subsequently, multiple frontend components required conversion before our first page went live. The UI design was kept the same to reduce the number of variables on this project.

Components

The dashboard comprises of several components, including:
  • Navbar
  • Sidebar
  • Dropdown menus
  • Modals
  • Breadcrumbs
  • Banners
  • Toasts
  • Alerts
  • Tooltips
  • Placeholders / skeletons

Some of these components contained complex business logic previously written in C# in the Razor pages. For example, the sidebar contains a list of features that are only available at certain tiers of the membership plan. The logic was rewritten into the React components using Typescript. Several components across the application would need to contain display logic that depended on information about the logged-in user account. This meant that a global state was required to store information about the user.

Global State Management with Redux

At the start of the migration process, I decided to use React Context to store the global state. As the migration process developed, I soon realised that Redux would be more appropriate for the following purposes - handling a large amount of state, containing complex update logic, and having the ability to dispatch state changes from any component. I swapped out Context for Redux Toolkit, resulting in a much more robust state management solution.

The Redux store comprised of consumables, plan, and user account information.

Unit tests

I used Jest and React Testing Library to create unit tests for all of the new Typescript components. Components that were dependent on global state were tested inside a redux provider, which allows custom test cases for every state. Over 400 unit tests were written in total.

Jest unit tests
Jest unit tests

Handling Forms with React Hook Form

The dashboard includes several different form types. One form functions as a calculator and includes number inputs and radio buttons. Another form is for adding clients and consists of text inputs and an image uploader.

I decided to use React Hook Form in this app for the following reasons: registering each input to a property of the model feels robust, the ‘required’ property options for validation offer great flexibility, and the documentation is easy to understand. The handle submit method only fires when validation passes, so you have confidence that posts to the API are only happening when they should.

Live updates with SignalR

To handle live display updates when consumables are spent, we used SignalR websockets via a Typescript interface. For example, when a credit is spent, the credits hub dispatches a method to update the consumables state in Redux. Every component that makes use of the credits property in Redux is then automatically re-rendered with the new value.

Outcome

The conversion process is still ongoing; however, the adoption of React with TypeScript has given us a modern codebase that aligns with industry standards and sets the foundation for future enhancements.

Technologies Used:
  • React
  • Typescript
  • Redux Toolkit
  • Jest
  • React Testing Library
  • React Hook Form
  • ChartJS
  • SignalR
  • SCSS
Back to all projects