How to Build a Calculator App with React and Redux

In this tutorial, we will be building a simple calculator app using React and Redux. The calculator will have basic functionality such as addition, subtraction, multiplication, and division. The app will also have a history panel to display the previous calculations.

Prerequisites

To follow along with this tutorial, it is recommended to have a basic understanding of JavaScript, React, and Redux. You will also need to have Node.js installed on your machine.

Setting up the Project

First, let’s set up the project by creating a new directory and initializing a new React project:

mkdir calculator-app
cd calculator-app
npx create-react-app .

Now, let’s install the required dependencies for Redux:

npm install redux react-redux

Project Structure

Inside the src directory, delete all the default files except for App.js and index.js.

Create a new directory called redux inside the src directory. Inside the redux directory, create the following files:

  • actions.js: This file will define the actions for our calculator app.
  • reducers.js: This file will define the reducers for our app.
  • store.js: This file will create the Redux store.

Finally, update the App.js and index.js files to reflect the following changes.

App.js:

import React from 'react';
import Calculator from './Calculator';

function App() {
  return (
    <div className="App">
      <Calculator />
    </div>
  );
}

export default App;

index.js:

import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import store from './redux/store';
import './index.css';
import App from './App';

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
);

Building the Calculator Component

Now, let’s start building the calculator component. Create a new file called Calculator.js in the src directory and update it with the following code:

import React, { useState } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { addNumber, clear, equals } from './redux/actions';

function Calculator(props) {
  const [input, setInput] = useState('');

  const handleClick = (value) => {
    setInput(input + value);
  };

  const handleClear = () => {
    setInput('');
    props.clear();
  };

  const handleEqual = () => {
    props.equals(input);
    setInput('');
  };

  return (
    <div>
      <input type="text" value={input} disabled />
      <br />
      <button onClick={() => handleClick('1')}>1</button>
      <button onClick={() => handleClick('2')}>2</button>
      <button onClick={() => handleClick('3')}>3</button>
      <button onClick={() => handleClick('4')}>4</button>
      <br />
      <button onClick={() => handleClick('5')}>5</button>
      <button onClick={() => handleClick('6')}>6</button>
      <button onClick={() => handleClick('7')}>7</button>
      <button onClick={() => handleClick('8')}>8</button>
      <br />
      <button onClick={() => handleClick('9')}>9</button>
      <button onClick={() => handleClick('0')}>0</button>
      <button onClick={() => handleClick('+')}>+</button>
      <button onClick={() => handleClick('-')}>-</button>
      <br />
      <button onClick={() => handleClick('*')}>*</button>
      <button onClick={() => handleClick('/')}>/</button>
      <button onClick={handleClear}>Clear</button>
      <button onClick={handleEqual}>=</button>
    </div>
  );
}

const mapDispatchToProps = (dispatch) =>
  bindActionCreators({ addNumber, clear, equals }, dispatch);

export default connect(null, mapDispatchToProps)(Calculator);

In the Calculator component, we are using the useState hook to manage the input value. We have also defined three event handlers:

  • handleClick: This function appends the clicked value to the input string.
  • handleClear: This function clears the input string and calls the clear action.
  • handleEqual: This function triggers the equals action with the current input string.

We are also mapping the addNumber, clear, and equals actions to the component props using the mapDispatchToProps function.

Now, let’s define the actions in the actions.js file.

actions.js:

export const addNumber = (number) => {
  return {
    type: 'ADD_NUMBER',
    payload: number,
  };
};

export const clear = () => {
  return {
    type: 'CLEAR',
  };
};

export const equals = (input) => {
  return {
    type: 'EQUALS',
    payload: input,
  };
};

The addNumber action takes a number argument and returns an action object with the ADD_NUMBER type and the number as the payload.

The clear action does not take any arguments and returns an action object with the CLEAR type.

The equals action takes an input argument and returns an action object with the EQUALS type and the input as the payload.

Next, let’s define the reducers in the reducers.js file.

reducers.js:

const initialState = {
  history: [],
  result: 0,
};

const calculatorReducer = (state = initialState, action) => {
  switch (action.type) {
    case 'ADD_NUMBER':
      return {
        ...state,
        result: Number(state.result + action.payload),
      };
    case 'CLEAR':
      return {
        ...state,
        result: 0,
      };
    case 'EQUALS':
      const result = eval(action.payload);
      return {
        ...state,
        history: [...state.history, action.payload + ' = ' + result],
        result: result,
      };
    default:
      return state;
  }
};

export default calculatorReducer;

The calculatorReducer function takes the state and action as inputs and returns the updated state based on the action type. In the case of the ADD_NUMBER action, the result is updated by adding the payload to the current result. In the case of the CLEAR action, the result is reset to 0. In the case of the EQUALS action, the input is evaluated using the eval function and the result is added to the history array.

Finally, let’s create the Redux store in the store.js file.

store.js:

import { createStore } from 'redux';
import calculatorReducer from './reducers';

const store = createStore(calculatorReducer);

export default store;

The createStore function from Redux is used to create the store by passing in the calculatorReducer function.

Displaying the Results

In the Calculator component, let’s add a new section to display the previous calculations.

Update the Calculator component with the following code:

function Calculator(props) {
  // ...

  return (
    <div>
      <input type="text" value={input} disabled />
      <br />
      {/* ... */}
      <br />
      <button onClick={handleClear}>Clear</button>
      <button onClick={handleEqual}>=</button>

      <br />
      <h3>History</h3>
      {props.history.map((item, index) => (
        <p key={index}>{item}</p>
      ))}
    </div>
  );
}

In the JSX code, we have added a new heading element <h3> to display “History” and a mapping function to render each item of the history array as a separate paragraph element <p>. The key prop is set to the index to provide a unique identifier for each item.

Also, in the mapStateToProps function in the Calculator.js file, let’s add the following code:

const mapStateToProps = (state) => {
  return {
    history: state.history,
  };
};

export default connect(mapStateToProps, mapDispatchToProps)(Calculator);

The mapStateToProps function maps the history array from the state to the history prop of the calculator component.

Now, when you run the app using npm start, you should see the calculator interface with an input field to enter values and buttons to perform operations. The history panel will display the previous calculations.

Running the App

To start the development server and run the app, open your terminal and run the following command in the project directory:

npm start

Now, the calculator app should be running on `http://localhost:3000`.

Conclusion

In this tutorial, you have learned how to build a simple calculator app using React and Redux. You have implemented the basic functionality of the calculator, including addition, subtraction, multiplication, and division. You have also added a history panel to display the previous calculations. By following this tutorial, you can further enhance the calculator app by adding more advanced operations or improving the user interface.

Related Post