Zach Marino

Loading Iframe content...

introduction

Abacus is a full-stack spreadsheet application coded in Typescript, using Express for the API and React for the frontend. It was created for my software engineering class in a group of 3. The other two worked on the backend logic, and I handled the API and frontend. We had to include a set of core requirements and 3 additional features. Our additional features were cell styling, undo/redo, and import/export functions.

code snippets

We were free to create the frontend whichever way we wanted, so long as it rendered what the backend did. Most other teams decided to re-render their spreadsheet whenever a change occurred, but we decided to take a different approach. Since our project had a large emphasis on individual cell customization, we wanted to make sure it was possible to re-render an individual cell when that was the only change. The whole spreadsheet would still need to be re-rendered when cell locations changed, but if the only change was the property of a single cell, then there was no need to re-render everything else.

We implemented this by creating two React contexts, SelectedCellContext and GridContext.

SelectedCellContext would track the currently selected cell and handle updates. Specifically, I had a function called updateSelectedCellProperty(), which would take in a property and a value and pass that into a POST request, which would update the property in the backend. updateSelectedCellProperty() would then increment a useState hook called setUpdateTrigger, which would trigger the context to fetch the updated cell data via another function. That function would set the cell data in a useState hook, and that hook is then passed into the instance of the cell.

Abacus Code Snippet 1

Entire spreadsheet re-rendering is actually handled in the spreadsheet component itself using useMemo hooks, but we needed a way to track when to re-render. This was especially the case when the number of rows and columns would change. GridContext would track this, and GridDataProvider would support it too. The custom hook below would handle the initial render of rows and columns.

Abacus Code Snippet 2

Our API was a pretty straightforward Express router setup. My partners created public-facing methods to access the Spreadsheet Model and the API calls those getter and setter methods. This separation between the backend and frontend lets us sanitize inputs and outputs, preventing errors in the backend or frontend from affecting each other. updateSelectedCellProperty() (from SelectedCellContext) would send POST requests to a large route that had a switch case for each possible cell property.

Abacus Code Snippet 3

layout

Resizing

I added breakpoints to the header so that if the screen got smaller, all of the functions would adapt. We were only making a desktop application, so I added minimum width to the breakpoint and moved on. To position the header above the actual spreadsheet, I created a split-screen layout component that structured the page using flexboxes. Besides being the easiest approach, I did this with a dedicated layout component because it is best practice to have position logic separated from component logic to retain the reusable nature of components.

takeaways

  1. 1. With Figma dev mode, spending time polishing a Figma prototype can not only reveal interface issues, but also save you time coding, as a polished prototype will have a lot of the layout and responsiveness ready to go via the provided CSS.
  2. 2. When developing the frontend for dynamic applications, there is much more involved than just the layout and styling. How you render the dynamic elements can make or break the efficiency of your system.
  3. 3. I believe that when designers know how to code, they can significantly improve the coding experience for developers by designing with the implementation in mind.