The Full Stack React App: A Front-End Postmortem
Building a full-stack React app? This article is Part 1 of a 2-part Postmortem of my project, covering the project’s front end. I look back at successes and failures I faced building my commission dashboard’s front end, so you can learn from my process and improve your Product, Design, and Development capabilities.
“I end each and every project with a thorough Postmortem.”
I would be hard-pressed to trust anyone who claimed this.
The excitement of starting a brand new project can sometimes eclipse the chore that is carefully reviewing completed work, but studying the past is critical to building skills fast. Whether it’s a web app, a shell script, a sales call, a Smash Bros. match, whatever the venture, a thorough Postmortem always generates a wealth of data on immediate strengths and weaknesses. This contributes to the development of skill and wisdom that will inform better decision-making in the future.
I recently completed the front end of a full-stack React app available for use later this year. Paywatch is a commission and income-tracking app that helps salespeople monitor their earnings. Below is a demo of the Paywatch front end:
And, here is a rundown of the tech used:
- React Redux for state management
- Build Environment (through webpack) configured from scratch, create-react-app/Gatsby.js not used, no boilerplate config used
- CSS in JS with styled-components, layouts created with CSS Grid and Flexbox. Blueprint.js for Date Picker, Text Inputs
- A mix of Class and Const Components for stateful and stateless components, some utilizing lifecycle methods
- React Router for client-side routing
- Victory D3.js library for data visualizations
- Unit and Integration testing with Jest, E2E Testing with Cypress.
- In Construction Now…
I wore 3 distinct hats during the creation of the Paywatch front end, handling Product, Design, and Development. In hopes of enriching others I share my experience in this detailed (hopefully not long-winded..?) Postmortem. It is divided into these sections:
Product: So…what and why is this thing?
1. Customer Discovery
2. Similar Apps
Design: How should users use it?
1. Sketch Mockup
2. Principle PrototypingDevelopment: Bringing ideas into the world
1. Presentation Logic
2. Business Logic
I’ll be covering The Process, The Good, and The Bad for many sections above. Let’s begin.
Product: So…what and why is this thing?
Ranking the amount of experience I have in each department, from most to least, it’d be Development, Design, and finally Product. So any pointers in this department would be valuable 😏
There were 2 forms of “Product” work I conducted before designing the app: Customer Discovery and studying Similar Apps.
1. Customer Discovery
In my last semester of grad school I spent every waking moment absorbing any and all content Y Combinator was putting out about building a company…each of the Stanford class videos, all of Paul Graham’s essays, Hacker News, whatever got me familiar with others’ processes of building what people wanted. Among the firehose of information was Eric Ries’ book Lean Startup, where I was introduced to Steve Blanks’ Customer Development framework.
Steve states that the early, Customer Discovery stage is the stage in which one learns “what the high-value customer problems are, what it is about your product that solves these problems, and who specifically your customer is.” I used this component of the framework in answering several key questions prior to building Paywatch, a commission and income-tracking app that helps salespeople monitor their earnings.
- The Process
So again, there were 3 questions for me to answer:
- what the high-value customer problems are
- what it is about your product that solves these problems
- who specifically your customer is
Here were my client’s assessment of needs.
- what the high-value customer problems are:
Upcoming paycheck is not clear ahead of time because commission structures can be confusing
Determining how many sales are needed for earning a specific amount of money is tedious, must be re-calculated anytime commission structures change
In-house software is inaccessible outside of work, data does not transfer when job is changed
In-house software calculating paycheck does not account for taxes, social security, etc., so it looks like you’re making more than you are - what it is about your product that solves these problems:
Upcoming paycheck shown in realtime based on sales
Commission plan editor helps easily create new commission structures
Clarifies sales needed to reach earnings goals (amount of sales adjusts based on configured commission structure) - who specifically your customer is:
Inside and outside salespeople
Salespeople selling products/services with simple commission plans
Possibly salespeople selling product/services with short sales cycles, generally
2. Similar Apps
I looked for apps that had already solved problems Paywatch aimed to solve. The apps themselves would give me an idea of how others had attempted to solve the problems. App popularity would suggest to me whether there was a problem in the first place.
- The Process
I found many apps that educated me on both questions.
They all looked perfectly functional. But, the user-friendly ones lacked helpful data visualizations, and the ones with helpful data visualizations were not user-friendly. One that was just right, was an app called Tip Tracker.
This app is so simple and “kid-proof”-looking. Big buttons, UI components familiar to most, only one crowded screen — it’s very clear what the app’s job is and how to use it. But it was built for service employees in mind, not salespeople. And it is mobile-only (which can be a strength depending how one looks at it). Maybe there are some ways this app’s execution can be improved upon…
Now, let’s move onto Design, a department I have a bit more experience in.
Design: How should users use it?
After I defined the problem my app aimed to solve, it was time to work on its Design. I’m using the word Design to refer to the UI/UX subset of the design field. The distinction between UI and UX is defined in this video from Jesse Showalter:
“A UI Designer’s purpose is to receive all of the [end user’s] needs…that somebody like the User Experience Designer has done, and then take that and put that into an attractive and aesthetically pleasing platform to be received by users.”
This graphic from UX Collective may also help understand the differences.
With the end user’s needs understood, I built out a mockup in Sketch and a prototype in Principle.
1. Sketch Mockup
Notes for the curious:
A “Mockup” is a high fidelity, non-interactive representation of a web page. A wireframe differs from a mockup, differs from a prototype. No wireframes were created for the Paywatch front end but when I make them I use Lucidchart.
“Sketch” is great piece of design software popular for making Mockups. But everyone uses “Adobe Photoshop” for creating images, right? While Photoshop is great for raster/bitmap graphics, Sketch is mainly for manipulation of vector graphics, common in web design. A fairer comparision may be Sketch vs. Adobe XD or Illustrator. Figma and Invision are other heavy hitters…but Sketch’s ease of use and plugin ecosystem has won me over personally.
a) The Process
I have been using Photoshop for 15 years, and Sketch for 5. But just as we have all used pens since grade school, we have not all developed the ability to create beautiful art with a pen.
Just the same, I know how to use design software pretty well at this point — which is a feat absolutely — but I am not Senior-Designer magnificent at creating an original, attractive interface (UI of UX/UI). Thankfully there is the other half of the equation, UX.
For fellow developers lacking in artistic vision, you may find UX more accessible because good UX often follows universal design principles. For example, The Eight Golden Rules of Interface Design or these Design Principles by Jaxon White. Being universal, they may help shrink an infinite world of possibilities into a smaller territory. It gives UX a structure, a framework, a constant in a discipline full of variables.
Anyways enough theory. What was my process for making the Sketch Mockup?
Design resource repos like Sketch App Sources and Dribbble (here’s mine) host work submitted by designers. These are free and, depending on the artist’s preferences, available for use by others. If used, it’s a best practice to credit the original designer.
So, what I’m not going to do is find a design from one of these repos and just code it. What I am going to do is find a design that looks like it could be remixed to fit the needs of my end user, use my UX and Sketch skills to remix and build on top of it, hire an Illustrator off Fiver for some graphics (UI work), and code it. Which is what I did.
I wanted the Paywatch front end to have a dashboard for users to input and track their income all in one screen. Bhavna Kashyap designed a simple and clean dashboard I found on Sketch Repo that looked promising:
I remixed Bhavna’s design into the below:
The component in which data is inputted has some simple pages:
And the Data Visualization would change based on the span of time selected:
It was a design I was ultimately content with.
b) The Good
- The Design was complete before Development
Elementary, I know, but when I started programming I’d get started building right away and design as I coded. This was such a bad idea for reasons that even non-developers can understand…I mean not only the look but the functionality of different pieces of the app could change by the hour, resulting in lots of extra work. It made projects take a lot longer than they would have had I completed the design at the onset. I did not make this mistake this time.
- The Design complied with design principles
Going back to Design principles:
a) The colors all work together nicely
b) The typography is inoffensive and fitting
c) The motion (seen in the prototype later) is not jarring or overly complex
d) There is a healthy amount of negative space around the components
e) There is enough contrast for boundaries to be clear
f) There is consistency across a single page, and among the Home and App pages
Again, I myself significantly redesigned Bhavna’s design so I am not taking credit for all of it. But, I am very satisfied to have modified a design in such a way that it continues to respect design principles and properly suits the purpose of my app.
- The Design was a massive improvement from the first effort
A demo of the first version from 3 years ago, also built in React:
I mean it wasn’t…tear-inducing I think? But it was very amateur. I was inexperienced, what can I say!
c) The Bad
- Design did not consider technical limitations
I love how the calendar design looks:
But there was a low chance I was going to find an existing library that could replicate this perfectly. And, I ultimately didn’t, but I tried to get close without undertaking the task of creating my own calendar app.
Next time I’d rather get the design accurate before coding so that I know how it’ll end up looking.
- Design process not formalized
I mentioned design principles I followed earlier. But I’ll admit to going with my gut on some of it, accidentally following design principles. I’m not saying I’m a natural…I am saying this process should be formalized.
- Design process took way too long
Thinking technically is familiar to me, while thinking in Design is less so. Thankfully I’ve internalized some knowledge and will create my next design a lot faster.
2. Principle Prototyping
Notes for the curious:
As I mentioned earlier a wireframe differs from a mockup, differs from a prototype. A “Prototype” can be considered like an interactive, clickable version of a mockup. You can click on it and it will change as it would when the site’s coded.
“Principle” is the app I use for building prototypes. InVision is probably the most popular, Flinto, Framer are other ones out there.
a) The Process
First, here is a video of the prototype:
I didn’t prototype every click, I just wanted to see how the animations looked to make sure they weren’t off or weird in anyway. And to make sure I had access to animation libraries that could pull it off.
b) The Good
- Prototype created with technical limitations in mind
I knew, ahead of time, the transition animation libraries that would be able to produce the effects in the prototype. And so when I coded the animations they looked as I had planned them to. Nice.
c) The Bad
- Prototype may be too simple?
The prototype displays some pretty basic stuff, not complex at all. Maybe it’s just enough, maybe it’s not enough, I’m not sure…
Now that Design is complete, we’re ready to develop this thing already.
Development: Bringing ideas into the world
Finally, to the Development stage. I’m going to break this section up into Presentation Logic and Business Logic. This section is going to be long so let’s get to it.
1. Presentation Logic
The presentational, “dumb” components as the Internet says.
a) The Process
I am very comfortable with HTML and CSS I’d say, making the UI development process go quickly. With my design fully fleshed out, I fired up my fave code editor Vim, set up my build environment, and got started.
One minor new thing I wanted to try out this time was a file structure methodology called “Atomic Design”. In Atomic Design with React, Danilo Woznica explains Atomic Design as a methodology helping build consistent, solid and reusable design systems by separating the components into atoms, molecules, organisms, templates and pages. Basically, the smallest unit of a page is an atom, which could be located inside a molecule, which is a part of an organism, and so on.
I like systems like this because they help me keep everything organized and when others read my code it can be made some sense of. It’s nothing revolutionary but it helps.
For example, here is the right-most vertical panel of the Paywatch front end, a component with some settings and a small built in todo app.
And here is an “atomic” view of the component:
As you can see, parent components get placed in the templates
directory, child components of templates
get placed in theorganisms
directory, and so on. Looks like I missed themolecules
tier for this one but you get the idea.
Here is how these components are organized in my repo:
.
├── components
│ ├── atoms
│ │ ├── Milestone.js
│ │ └── UserItem.js
│ ├── organisms
│ │ ├── Calendar.js
│ │ ├── InspirationBox.js
│ │ ├── MilestoneList.js
│ │ └── MilestoneParent.js
│ ├── templates
│ ├── App
│ └── RightPane.js
I mentally categorized my project according to Atomic React and coded away. Yielding much good and many lessons for improvement the next time around.
b) The Good
- Actions Box
The sole function of this component is to take the user’s input and calculate their income based on the selected commission structure. An interface was needed for this.
Here is the initial design from Sketch:
You can find a clickable prototype in Invision here.
And the final product here:
This component is divided into 2 child components, one on the left and the other on the right. The left hand side handles the button logic, the right hand side handles presentational logic and also the entry of hours worked and sales made. Here is the initial plan that I followed.
How this works is that the left and right hand side child components are rendered based on the Redux store, which is changed by the Actions triggered by the left hand side buttons. With each button press, changes are made to two Redux store values, the “journey” (Whether you are in the Log Income, Manage Income, or At a Glance track) and the “page numbers”. The actions change the journey and page number, and then render a different left and right hand component.
The spinner appears and disappears based on a boolean in the component’s local state, with a simple delay created by setTimeout()
. I have an actually honest, true loading spinner for the API calls to the Quotes API, discussed later (it is a part of the promise chain).
A quick note that I still not do like how many left hand side components there are; each panel has only 2 or 3 buttons and I believe this could be simplified into fewer components. Maybe say by the use of a .map
function to create buttons based on an object with button names, colors, and actions. I chose instead to just create separate components based on each journey and page number, and leave it open to refactoring later. There was enough custom logic for each button that I thought it’d be easier to just have each component store that specific logic for now.
As you can see the Actions Box handles the input where the user enters the hours worked and sales made. The logic behind that input, the calculation of the income, and the Data Visualization’s re-render based on the input are all covered in the Business Logic section later.
- CSS in JS is alright!
For my first React project years ago I used Sass for the CSS and had a hierarchy similar to Atomic Design to keep .scss
file locations clear in my head. Here is an abridged version of myscss
file tree at the time:
.
├── abstracts
│ ├── _media-queries.scss
│ ├── _mixins.scss
│ └── _variables.scss
├── base
│ ├── _base.scss
│ └── _typography.scss
├── components
│ ├── _all.scss
│ ├── _pay-panel.scss
│ ├── day-panel
│ │ ├── _all.scss
│ │ ├── _day-panel.scss
│ │ ├── _day-tomorrow.scss
│ │ ├── _day-yesterday.scss
│ │ └── today
│ │ ├── _default.scss
│ │ └── _start.scss
│ ├── history
│ │ ├── calendar-wrapper.scss
│ │ └── day-views.scss
│ └── months
│ └── april18.scss
Looking at my old code I observe 1) many className
attributes on many divs, 2) deeply nested .scss
rules, 3) a need for constant coordination between class names in the .scss
file and components’ className
attributes.
While maintaining and contributing to a local open source project I discovered Emotion, a library for writing CSS in JS. It was my first experience with CSS in JS and I ended up enjoying it, so I used Styled Components for the Paywatch front end. I enjoyed the component-based architecture of CSS in JS, it pairs nicely with the component-based architecture of my React apps, each component’s CSS residing in that component. Styled Component’s ThemeProvider
even helped me develop a Dark Mode for Paywatch, which I’ll talk about later.
const Flexbox = styled.div`
display: flex;
flex-direction: column;
justify-content: center;
`;// A sample flexbox container
Are there cons? Of course —its syntax can be weird, there is a smaller community around it, and maybe it’s here today and gone tomorrow. But I found it useful for Paywatch front end.
- Dark Mode
Dark Mode wasn’t initially in the scope of the project. But I was looking through thenounproject for icons to put in my Admin.js
component at the top right and saw a light bulb. I figured it would be fun to try out. I wish every app had a Dark Mode.
I read Atharva Kulkarni’s article on designing a Dark Mode to whip something up quick in Sketch.
And found that Styled Component’s ThemeProvider
would make this easy to implement.
Here is how I did it:
Step 1: Import the ThemeProvider
package and create an object of hex codes in the parent component:
import styled, { ThemeProvider } from "styled-components";...const dark = {
maintext: "#DBDBDB",
rightpanebg: "#060B10",
centerpanebg: "#03111F",
leftpanebg: "#00021B",
text: "#BDBDBD",
inspbox: "#FFFFFF",
actionbox: "#BDBDBD",
milestone: "#F8F8F8"
};const light = {
maintext: "#333539",
rightpanebg: "#EFEFEF",
centerpanebg: "#FFFFFF",
leftpanebg: "#121432",
text: "#333539",
inspbox: "#333539",
actionbox: "#959595",
milestone: "#FFFFFF"
};
Step 2: Wrap the ThemeProvider
element around components that we want to have access to these hex codes later
<ThemeProvider theme={this.props.storemode ? dark : light}>
<Flexbox>
<LeftPane />
<CenterPane />
<RightPane />
</Flexbox>
</ThemeProvider>
Step 3: Make the Styled Component on the child component dependent on props:
const Flexbox = styled.div`
display: flex;
flex-direction: column;
background-color: ${props => props.theme.rightpanebg};
padding: 1.5%;
max-width: 370px;
overflow: hidden;
`;
You can see the ternary operator in the ThemeProvider
wrapper that basically toggles which object will be passing props down to the child component. In my case this.props.storemode
is a boolean in my Redux store toggled by an Action. If storemode == true
, then use the “dark” object, if not, use the “light” object. Good stuff.
- Animations easier than expected
There are many complex animation libraries out there, making it quite tempting to create something really eye-popping. But that would violate the design of the app which is intended to be pleasing but conservative. So, I stuck to this basic animation prototype for my todo-style mini app inside the Paywatch front end.
I chose react-pose
, a simple animation library to do this. Here is the final product:
React Pose’s docs state “By default, Pose doesn’t require you to explicitly define the animations used to transition between two states. Instead, it automatically creates an animation based on the properties being animated.”
So I define two states, and React Pose handles the transition:
const AnimatedDiv = posed.div({
enter: {
x: 0,
transition: {
x: { type: "spring", stiffness: 100, damping: 30 }
}
},
exit: {
x: 500,
transition: {
x: { type: "spring", stiffness: 100, damping: 30 }
}
}
});...return (
<>
<Confetti active={this.state.completed} config={config} />
<AnimatedDiv pose={this.props.show ? "enter" : "exit"}>
<Milestone celebrate={this.celebrate} flip={this.props.flip} />
</AnimatedDiv>
</>
);
The celebration()
function (not shown) invokes the toggle of a boolean in local state. This function is passed as a prop down to the child component, and called on click. The confetti is from the react-dom-confetti
package, triggered when a prop goes from falsy to truthy.
- Handling SVGs in React
For the settings bar at the top, I initially had 3 PNG files in a flexbox.
Without a dark mode this would have worked fine. With a dark mode, it was smarter to toggle colors on an SVG instead of loading a different PNG based on the dark mode Redux store prop.
I looked to React SVGR, a CLI tool, to transform my SVGs into React Components. The tool would take an SVG and output something like this:
import * as React from "react";function SvgComponent(props) {
return (
<svg width={19} height={30} {...props}>
<path
d="M9.498 0C7.194 0 4.89.82 3.075 2.461a9.284 9.284 0 00-1.283 12.385 9.334 9.334 0 012.002 5.778c0 .497.201.975.557 1.326.202.199.447.342.71.435V27.5c0 1.326 1.084 2.5 2.397 2.5h4.11c1.285 0 2.366-1.181 2.366-2.5v-.625-2.5-
// abridged
fill="#FF5733"
fillRule="nonzero"
/>
</svg>
);
}export default SvgComponent;
It is then available for use in another component. The fill
property, controlling the color of the SVG, can be changed to "fill"
to ingest a hex code as a fill
prop. So now, it can do this:
- Presentation logic was relatively straightforward
Overall, the development of the UI was painless, fun, and not intellectually all that demanding. It was immediately gratifying to design and build, with music playing in the background all along.
c) The Bad
- Blueprint.js CSS import overwrote my CSS
After I found a component library with a Date Picker component I liked, I imported the package and accompanying CSS. Then blueprint.js proceeded to immediately overwrite a lot of my existing CSS.
Rookie mistake.
- Unforeseen Milestone functionality complexity
The original plan for Milestones, the mini to-do list app inside of Paywatch, was to create a list of 3 items the user could delete. The user should have been able to delete any of the items, and the UI should have adjusted to the deletion. Like this:
Slick! I started to work on this, when I realized that the animation, however, would take a moment to get right. The third item would have to slide up upon the exit of the second item. The second item would have to slide up upon the exit of the first item. And the items’ new positions meant the item would need to animate according to a new set of rules, based on its new slot.
While in time I will come up with a smart, concise solution, I opted instead to spend the time on other parts of the UI. I will complete this properly at the product’s launch.
2. Business Logic
Ah yes, how could I forget the programming part.
While this project obviously isn’t a high tech ML/AI project built on the blockchain, there are still things to talk about.
a) Data Flow
Redux was used liberally throughout the app to share pieces of state with components that did not share a nearby, parent component.
Sometimes there was no question Redux was the best way to manage state, like in the case of Dark Mode where components across the repo need to be aware of a boolean value. Other times, it was simply a convenient option that would prevent cumbersome prop drilling, like with the Actions Box discussed in the Presentational Logic section.
Here is a simple Action, used to increment the Page in the Actions Box component:
export function incPage() {
return { type: "INC_PAGE" };
}
And here is the accompanying Reducer:
export default function pageReducer(state, action) {
if (typeof state === "undefined") {
state = 0;
}
switch (action.type) {
case "INC_PAGE":
return state + 1;
case "DEC_PAGE":
return state - 1;
case "RESET_PAGE":
return (state = 0);
default:
return state;
}
}
There is plenty of complexity left to discover in Redux but my app has not called for it just yet.
b) Calculating Income based on User Input
The logic behind the main purpose of this app. This will be divided into 3 sections: Inputs, Calculations, and Data Visualization.
- Inputs
I used 2 components from the blueprint.js library, the HTML select and the “Editable Text” inputs.
The JSX for the HTML Select is simple:
<div className="bp3-select bp3-minimal bp3-fill">
<select onChange={this.updateHours}>
<option value="0">0</option>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
<option value="6">6</option>
<option value="7">7</option>
<option value="8">8</option>
<option value="9">9</option>
<option value="10">10</option>
</select>
</div>
The updateHours()
function is below:
updateHours = event => {
let hours = parseInt(event.currentTarget.value);
this.props.dispatch(hourAddAction.addHour(hours));
};
The action simply shoots it into the store, no validation required.
The Editable Text component is close to the same:
<EditableText
maxLength={3}
onChange={this.updateSales}
value={this.props.statesale}
/>
You will notice that this is a controlled component. Also, the updateSales()
function does a bit of cleaning for this input via regex:
updateSales = value => {
let input = event.target.value.replace(/[\W|\D]/g, "");
if (input == "") {
input = 0;
}
this.props.dispatch(saleAddAction.addSale(input));
};
Again the action sends the input to the store.
- Calculations
Logic for the calculations exists in the same component, inside the render function:
let hours = this.props.storehour;
let sales = this.props.storesale;
let base, com, tax;this.calcBase = hours => {
return (base = parseInt((hours * 12.5).toFixed(2)));
};this.calcCom = sales => {
return (com = parseInt((sales * 20.75).toFixed(2)));
};this.calcTax = () => {
return parseInt((tax = (base + com) * 0.12));
};this.calcTotal = () => {
return parseInt(base + com - tax);
};
These are all displayed in the table below.
<table className="bp3-html-table bp3-html-table-bordered bp3-html-table-condensed">
<thead>
<tr>
<th>Base Pay</th>
<th>Commissions</th>
<th>Taxes</th>
<th>Total</th>
</tr>
</thead>
<tbody>
<tr>
<td>${this.calcBase(hours)}</td>
<td>${this.calcCom(sales)}</td>
<td>${this.calcTax()}</td>
<td>${this.calcTotal()}</td>
</tr>
</tbody>
</table>
Similar logic exists in the “At a Glance” component. Except there is a bit of conditional rendering happening to ensure the correct article (a/an) is used before the number of hours:
<Note>
Demo Commission Plan currently selected.
<br />
Base Pay calculated based on
{this.props.storehour == 8 ? <span> an </span> : <span> a </span>}
{this.props.storehour} hour workday.
</Note>
- Data Visualization
Things get interesting here! Keep in mind, this is just the front end. I am building out the backend to this project now. I knew from working in Open Source that a fully built-out backend means handling data for data visualizations very differently than any way I would build this out immediately, with no backend. And so I opted for the simplest logic just to get by for now.
The state for the Bar Chart component houses the array of values for the bar chart:
constructor(props) {
super(props);
this.state = {
data: [
{ day: 1, earnings: 230 },
{ day: 2, earnings: 165 },
{ day: 3, earnings: 185 }
]
};
}
The Victory bar chart component itself has a bunch of props for styling. It also has an x-axis labeled with the days of the week, with help of the moment
package.
const dayArray = [];
var i;
for (i = -3; i < 2; i++) {
dayArray.push(
moment()
.add(i, "d")
.format("dddd")
);
}
When the Redux store’s hours or sales value changes, the component uses the ComponentDidUpdate
lifecycle method to run a function.
componentDidUpdate(prevProps) {
if (
prevProps.storehour !== this.props.storehour ||
prevProps.storesale !== this.props.storesale
) {
this.updateValues();
}
}
That function is large one, one you can find on line 34. This is what is happening:
- The existing state’s worth of the week’s data is copied (as state is immutable)
- Depending on the number of days, the object may or may not be mutated.
- Earnings are calculated for the most recent day with help of the
calculate()
function - The result is assembled into a new object and pushed into the state object via
setState()
- The chart re-renders with animation! ❤️ 📊
This part was the most fun to get working. It was immensely gratifying once it was up and running, and it had more in-depth JavaScript than the rest of the app.
c) API Calls
There is 1 API call made in this app to GET quotes from a Quotes API. Here is the end result of that call:
The call is a fetch()
call as shown below:
const API = "https://type.fit/api/quotes";
...fetchData = () => {
const that = this;
this.setState({ loading: !this.state.loading });let randomInt = Math.floor(Math.random() * 1642);fetch(API)
.then(response => response.json())
.then(data => this.setState({ data: data[randomInt] }))
.then(function() {
that.setState({ loading: !that.state.loading });
});
};
The setState calls first switches the loading spinner on, and then later switches it off after the promise resolves.
This was a pain to set up…seriously. Why?
- I actually had an initial, successful implementation with an API,
type.fit.com
. But the service moved to a different CDN provider and I ran into a CORS issue (missingAccess-Control-Allow-Origin
response header), bricking the implementation. - So, it was time to find another API and rewrite this. I found one called “They Said So”, paid the $5 for a month’s use, tinkered with the unreliable API with my new API key, and implemented the above. Unfortunately, the
quotes/search
endpoint I wanted to use doesn’t really work because the API doesn’t respect thecategory
ormaxLength
parameters (noted in the docs!) that would give me 1) quotes that are inspirational and 2) correctly-sized quotes. This resulted in this:
3. The initial API was fixed! Thank you ssokurenko for your work!!
As the front end is fleshed out I plan to later make calls to the taxee.io
API which gives me access to tax information by region. This will be used in calculating income of course.
d) Testing Suite
The End to End testing suite is in Cypress. The E2E tests I developed check for CSS changes, accurate output for identical inputs, and the creation of elements, generally.
Below is a sample:
it("Cancel milestone after creation", () => {
cy.get("[data-cy=createbtn]").click();
cy.get(".bp3-editable-text-content");
cy.get(".cancelbtn").click();
});it("Dark mode", () => {
cy.get("[data-cy=darktoggle]")
.click()
.as("DarkToggle");
cy.get("[data-cy=admin]")
.parent()
.should("have.css", "background-color")
.and("match", /rgb\(6,.11,.16\)/);
cy.get("@DarkToggle").click();
});
Unit and Integration testing are in its early stages, completed in Jest. I need to configure my webpack to play nicely with Jest via Babel.
e) Routing
React Router was used for the creation of two paths, the base and /app
. The implementation was very straightforward. Part 2 of this Postmortem will cover React Router’s integration with server-side code to successfully implement my existing 0Auth code.
And that is that! Thanks for reading, hope you gained something from this.