DEV Community

Cover image for Just the Tip — Building a Simple Tip Calculator in React
Shujaat Azim
Shujaat Azim

Posted on

Just the Tip — Building a Simple Tip Calculator in React

My strategy for learning anything new? Practice. When I'm studying? Flashcards, online tests, cramming. A new competitive game? Training mode, tons of back-to-back matches. Learning Japanese for my next solo trip? Apps, podcasts, shows. All of these are forms of practice, and I applied this same, simple strategy to the development of my programming skills post-bootcamp graduation.

Now that I've convinced you that practice rocks, I am going to walk you through one of my earliest practice projects that I built after completing my Flatiron bootcamp program -- a tip calculator! Yaaaaay! No? Not convincing you of my app's amazingness with my faux enthusiasm? Well, in fact, we don’t need to come up with a super-cool, ground-breaking, worldview-shattering idea in order to practice. If you sit and wait for that "AHA!" perfect idea moment, the one that spawned the likes of Google or Facebook or Instagram, you'll end up wasting even more time in procrastination land. I would know. I've been there. And I still visit from time to time.

Instead, we can simply take an app or idea that someone else has built and try to copy it using our own skills and knowledge. This will give us a deeper understanding of our current standing, as well as open up multiple opportunities to put our own 'flair' on an idea. In fact, if you're just starting out, you've probably already seen a bunch of to-do list tutorials for every imaginable language or framework. The reason there are so many is that people have figured out it's a good idea to re-build things for practice. That's not to say that you can't use or deploy your own creation; by all means, do so! Just be sure to give appropriate credit and don't copy line by line.

Oh, and that whole "someone else must have already thought of this” attitude shouldn’t ever stop you. After all, should Mr. Dorsey and friends have shied away from creating Twitter just because we already had AOL Instant Messenger and Facebook statuses? I think not! Let’s get started!

Here's a link to my github repo if you'd like to follow along:

Disclaimer: I'm assuming everyone reading this has working knowledge of React, create-react-app, and what "state" is/does. I also want to point out that I made this project when I was still a bit new, very soon after I graduated from my Flatiron Bootcamp. Therefore, I'm sticking to basic class components with state. I will be posting a follow up tutorial soon; it will refactor this project to use functional components, hooks, and other modern tidbits! Look forward to it!

Go into your terminal, and within whichever directory you choose, type create-react-app [name of your app]. I named mine "Just the Tip" -- I know, I'm so clever. After everything is finished setting up, open the file in whichever code editor you use. I use VS code, so while in the root of the project in my terminal I just type code . to get started.

Then navigate to your src → App.js file. This is where all the work is going to be done. I decided to only use this component since it’s a small app and doesn’t need to pass information around.

You will see that App.js is a functional component, also called a stateless component. We're going to change this to a class component. Again, we could use the new(ish) React hooks to keep track of state in a functional component, but we will keep it simple for the sake of brevity, clarity, and for the new-to-React folks. You should end up with something like this, after removing some of the superfluous code:

conversion of functional to class component
First, small step!

Now let’s take a moment to think about what would be needed for this app. In order to calculate tip, we’re going to need a few values — the total bill, the tip percentage, and the size of the party (if splitting the bill). These will allow us to get both the total tip and the tip per person numbers after a little basic math. Because we’re going to be storing and updating these values, we’re going to put them in state as empty strings.

pieces of state
State can be made like this nowadays, without a constructor.

You might be wondering, “Why empty strings? Aren’t we dealing with integers?” No? You weren't? Well, bonus points if you were wondering that, because it's a good question. While we could use integers, I figured that the empty strings would be a bit more flexible down the road. JavaScript is a weakly-typed language, which means it sort of just “knows” that the integer 42 is the same as the string “42” for most intents and purposes. This leads a bit more flexibility while sacrificing a bit of clarity. As you'll see below, I ended up using words ("Subpar", "Excellent", etc) to define a certain level of service versus using tip percentages. This was for UX purposes, and you might not elect to do the same thing - but JavaScript offers such flexibility by being weakly-typed.

Next, let’s build out some of the form so that we have a place for the users to enter some information. It’s important that we make sure that this form is "controlled," meaning that the inputs change their associated state values. We don’t yet have the functions that will do that change, yet, but we will take care of them in the next step:

A simple, controlled form.

We set the values of the inputs to a piece of state, making this a controlled form. We’ve also already put in the function names that will be called when something is typed into the field (ie “on change”). They’re all named something similar, but they are descriptive — anyone looking over the code instantly know which piece of the state the fields are responsible for. This is useful for many reasons, including debugging, peer review, and future refactoring.

Let’s go ahead and create those functions. They’re all going to take the value of the input that they are associated with, and set that piece of state using the setState() function. Remember, do not directly manipulate state with stuff like state.value = newValue! Use setState()! Additionally, they’re all going to take the event object as an argument so that we have access to, which will be whatever's typed into the input box.

event handler functions
Not very DRY… It might be possible to combine these into one handler.

You can check if the state is being updated by looking in your React Dev Tools or by using console.log() on a piece of state and watching it change in the console in real time. It’s important to note that, for now, we don’t have any validations for the input fields. This means we’re able to type whatever we want into them and the eventual submission of the form will still go through. We’re just going to assume that the users will know exactly what to put into the fields for now; the main goal here is to practice using React and getting something up and running.

If you're a little more advanced in your dev know-how, you'll also notice that these functions are all pretty similar - they're not very DRY ("don't repeat yourself"). Is it possible to combine these into one handler function? Maybe! Again, I'm going for explicitness here, but I'm hoping that you'll take away these little tidbits too!

Now, it’s time for some tip-generating logic. We need to create a function that will take all the state values and do some math on them to create our two desired values: a total tip value and a tip per person. Since we know a tip is the bill total multiplied by a certain decimal, we’re just going to make it ( * (this.state.service). Then, after storing this value in the state, we’re going to simply divide it by the party size to get the tip per person value. Finally, we’re going to make sure that this function is called upon the form’s submission. Sounds simple enough:

tip calculation function
The parseFloat().toFixed(2) ensures that we round to two decimal places!

We must remember that setting state is asynchronous, so putting the tip calculations directly into the setState function will correctly post the totalTip, but will come up as 0 or undefined for tipPP. This is because tipPP depends on the totalTip, and is calculated at the same time as totalTip is calculated (which is still an empty string at the time). To remedy this, we simply put the calculations into variables, which are synchronous, and set the state fields as those variables.

We can now update our rendering a bit, such that the form will call genTip on submission and to display the tip values under the form:

adding tip to display
Just two small changes, plus adding “required” to each field.

And we’re done! A fully functional app for calculating tip. You should have something like this:

the barely-styled final result
Simple, elegant, useful!

Pretty awesome! But why stop there? There’s lots of ways to make this simple app even better. For example, the user experience is a bit lacking — who wants to type 0.20 for 20% tip? And right now, the user can type in any string, such as words, into the fields and get $NaN as a result. Maybe we need some form validations, like <input type="number" />. Also, in order to clear the form, we need to reload the page — sounds like we need a “Clear” button. And why have the fields all in a line? It’s an eyesore!

These are just a few of the tasks that can help refine some more skills using React. Perhaps you’d also want to add on a couple styling libraries? Or throw on a backend to be able to save your tips and keep track of how much a user tips generally, for research purposes? That last one might be a big one, but it illustrates that the possibilities for refactoring even this super-simple app are endless. Here’s what mine looks like now:

one possible styling route
Very simple styling makes a big difference!

And after submitting….

adding sweet alert library
I used a package called Sweet Alert for this display.

Cool, right? So your next steps could be styling, form validations, some libraries, and perhaps a backend (not necessarily in that order).

I hope you've found this guide useful, whether you’re a fresh beginner or a seasoned developer in need of a quick review. It was certainly useful for me to write it up as it forced me to redo and rethink many of the steps I had taken previously, therefore cementing the knowledge even more. I will be revisiting this project and some of my other (slightly) more complicated ones in the future!


Top comments (0)