by Theran Brigowatz
How to build a calculator with React Hooks and the React Context API
If you are like me, when you first heard of React Hooks you were maybe a little bit ambivalent or confused by what all the hype was about. What’s the big deal if I don’t have to write out class components anymore? However, once I dove in and got to using them, I couldn’t really see myself going back to my pre-Hook days. In the immortal words of Blues Traveller, “The hook brings you back. I ain’t tellin’ you no lie.”
For a while, I was looking for a guide about how to use Hooks in connection with the Context API. After only finding a few examples out there that explained the concept fully, I decided to do what any good developer should do: pore over the docs and build something myself. Struggling through it and learning it on your own is one of the best ways to absorb knowledge. This is a guide for how to build the same project that I did with the use of Hooks and Context.
This project is going to be a basic calculator app similar to the iPhone calculator. Since this is just a simple desktop app I have replaced the % button with a back button. Though I wouldn’t use this to take the SATs, you could definitely add up the number of toes you have on it.
To get started we are just going to use
create-react-app. You can get started by running the following:
npx create-react-app calculatorcd calculatornpm start
File Structure & CSS
The file structure of the app should look like the following. In the
src folder create the following files or just leave the
src├── App.js├── index.js└── components ├── BackButton.js ├── Calculator.js ├── ClearButton.js ├── Display.js ├── EqualButton.js ├── FunctionButton.js ├── NegativeButton.js ├── NumberButton.js ├── NumberProvider.js └── styles └── Styles.js
If you want to follow along exactly you can also install Styled Components for the CSS.
npm -i styled-components
You can then add the Styled CSS from this link to the
Styles.js file or add your own.
Main App Structure
Calculator.js file should setup the display and number pad. It should contain all of the button types.
You will notice that all of the button components are added in here along with the number display. Each of the button components are essentially the same. They should all follow the same basic structure. The
zero-button gets a separate
div since we are using CSS Grid for the layout and it needs to span two columns. (PS — If you want to know more about CSS Grid I did a little article on the basics.)
You may notice that the
buttonValue props is only needed for the
FunctionButton components. Each of the buttons should follow the same basic structure with a unique name. You can reference the file structure up above to see which buttons are needed. The buttons should have the symbol written in the button component if they are not passed a
buttonValue via props. Create one of these for each of the button types in your file structure.
After this you should have the basic structure of a calculator. We are going to come back to the display in just a bit. Now we are going to get into the inner workings of the app and see how we can use our Hooks and Context.
Building the Context API Provider
We are now going to create the
NumberProvider.js . This is the heart of your app and where our functions are going to live. If you have never used the React Context API it is a great tool to help pass data from one component to another.
Think of when you have components that are nested within each other. In the past you would have to “prop drill” . This is when you pass the data or function through as props in down through nested components. This is hardly ideal, especially when you start to go several layers deep.
However, with this provider component, it allows you to pass data to any nested component, no matter how deep. This number provider will wrap our
App component. Now whenever we want to get data, or use a function that lives in the provider, it is globally available. This gets us out of having to “prop drill” through nested components. You maintain the single source of truth that is the essence of React. To get started you need to create the provider. It should look like the following:
The basic provider is created and any value that is passed in is now available to all nested components. In order to make this available we are going to wrap our
App component so it is globally available. Our
App will have this code.
Using the Context Provider
Now we can add in the code for our display. We can display the value by passing in the
useContext function from the new React Hooks API. We no longer have to pass in prop through nested components. The display should look like:
The number that you passed three levels up in the
NumberProvider is immediately available to the
Display component by calling
useContext and passing our created
NumberContext. Your number display is now up and running as it is showing
number which we have set to zero.
Now of course our calculator is showing a single zero. This is great if you are counting the number of hours of sleep I get with a new born son, but not so great if trying to add anything else, so let’s use some hooks going get this calculator calculating.
Getting Started with Hooks
If you haven’t used a hook before, it essentially allows you to get rid of the class syntax, and instead have state within functional components. Here we can add the following to our
NumberProvider.js file in order to create our first hook.
There might be some syntax you have not seen. Rather than writing out our class with state we break each part of state into its own smaller
number variable. There is also
setNumber which acts the same as
setState function, but now works for a specific variable, and can be called when necessary.
useState allows us to set an initial value.
We are now able to use this all in our function to pass the number button values into the display. In this app the calculator is using strings to get the input. There are checks to make sure that you can not have multiple
. in your number and that you do not have series of zeroes to start your number.
Building Button Components
Now you can call this function using the Context API in any of the nested components.
Now you have working string of numbers maker. You can see how you can start to inject the values that you set in the
NumberProvider into the other components of the app via the
useContext function. State and the functions that affect it are held in the
NumberProvider . You just have to call in the specific context that you want.
You can start to see how this would be great as you start to add more complexity to your app. Say you want a user component to check that you are logged in to use special features. You can create a separate provider that holds the user data and makes that available any nested component.
We can continue to add in functions to our calculator and pass them to the proper component through the
useContext function that is built in.
Completed Provider Functions
NumberProvider is found below and contains the following functions that are used with hooks.
handleSetDisplayValuesets the value that you are typing into the display. We are checking that it there is only one decimal in the number string and we are limiting the number length to 8 characters. Think of this as more a tip calculator than one to get you through your calculus exam. It takes in the
handleSetStoredValuetakes our display string and stores it so that we can enter another number. This is our stored value. It will be used as a helper function.
handleClearValueresets everything back to 0. This is your clear function. It will get passed to
handleBackButtonallows you to delete your previously entered characters one at a time until you get back to 0. This belongs in the
handleSetCalcFunctionis where you get your math function. It sets if you are adding, subtracting, dividing, or multiplying. It gets passed into the
FunctionButton.jsfile and takes in the
handleToggleNegativedoes just as the name implies. It allows you do so for either the display value or a stored value after a calculation. This of course goes in
doMathdoes the Math. Finally. Since this is only a simple four function calculator it is just using simple switch function depending upon the
functionTypethat we have in state. We are using
parseIntsince we are passing our number in as strings. Also we are rounding to only three decimal places, to make sure that we do not have crazy long numbers.
The Finished Display
You will also need a display. In this case it will show the
number and the
storedNumber along with your
functionType. There are a few check such as showing a 0 when you have an empty string as a number.
For brevity sake I am not going to include all of the button functions since they are pretty much the same as the
NumberButton.js file above. Just be sure that you pass in a
buttonValue prop when necessary, and that you are passing in the correct function from the above list.
See All the Code
If you would like to see the entire code for this project it can be found over in:
I hope that this clears up a bit about how React Hooks and the Context API can be used together. Using these built in React features offers several benefits.
- Simple to understand syntax and gets rid of the clutter of class components. No more super and constructors. Just a few clean variables.
- Easier to set and use state within and across components. No more messy prop drilling through multiple component.
- Eliminates need for Redux in small projects, where you don’t need to hold too much in complex state. You probably aren’t going to re-create Facebook with it, but it will do the job on small-scale apps.
Please let me know your thoughts or if there are any issues that you come across in the code. Hopefully this shone a bit of light onto something that you may not have been familiar with before. React Hooks and Context are great ways to simplify your React apps and write cleaner code.
Check out more of my work and other projects at https://theran.co.