Get up to 80 % extra points for free! More info:

Lesson 4 - Completing the React Calculator

In the last lesson, React calculator components, we started programming the React components of our simple calculator. We chose this as a simple enough project to test the basics of React.

In today's React tutorial we will finish our calculator. :)

src/calculator/CalculatorForm.js

The highlight will be the component itself for the calculator form, where we will connect all previous functionality and also define our calculations here.

In the last lesson, we prepared the construction of a calculator form and the operation of entering operand values into numeric fields. Now we will add the calculate() function, where we select the operator and perform the calculation directly. It's great to use a switch. The entire code, including imports, will look like this:

import React, { useState } from 'react';
import NumberInput from './NumberInput';
import Select from 'react-select';
import Result from './Result';

const CalculatorForm = (props) => {
    const [selectedOptionState, setSelectedOptionState] = useState({
        selectedOption: {
            value: '--Select an operation--',
            label: '--Select an operation--',
        },
    });
    const [resultState, setResultState] = useState(null);

    const options = [
        { value: 'ADD', label: 'Addition' },
        { value: 'SUBTRACT', label: 'Subtraction' },
        { value: 'MULTIPLY', label: 'Multiplication' },
        { value: 'DIVIDE', label: 'Division' },
    ];

    let calculate = () => {
        const { x, y } = props;
        switch (selectedOptionState.selectedOptionState.value) {
            case 'ADD':
                return parseFloat(x) + parseFloat(y);
            case 'SUBTRACT':
                return parseFloat(x) - parseFloat(y);
            case 'MULTIPLY':
                return parseFloat(x) * parseFloat(y);
            case 'DIVIDE':
                return parseFloat(x) / parseFloat(y);
            default:
                return null; // It should never happen here.
        }
    };

    const handleSubmit = (event) => {
        event.preventDefault();
        const result = calculate();
        console.log(result);
        setResultState(result);
    };
    const handleChange = (selectedOptionState) => {
        setSelectedOptionState({ selectedOptionState });
    };
    return (
        <div>
            <form className="CalculatorForm" onSubmit={handleSubmit}>
                <NumberInput
                    OnChange={props.xOnChange}
                    name="x"
                    label="First number:"
                    value={props.x}
                />
                <NumberInput
                    OnChange={props.yOnChange}
                    name="y"
                    label="Second number:"
                    value={props.y}
                />
                <Select
                    onChange={handleChange}
                    value={selectedOptionState.selectedOption}
                    options={options}
                />
                <input value="Count" type="submit" />
            </form>
            <Result value={resultState} />
        </div>
    );
};

export default CalculatorForm;

At first glance, it may seem a little complicated, but in reality, there is nothing we don't already know. :) It is worth noting that in the handleSubmit() function we use event.preventDefault(), which prevents reloading the page after sending user input. In order not to have to reinvent the wheel, we also import Select from the react-select library. Thanks to this add-on from one of the React libraries, the application will look nice and interactive.

src/App.js

Finally, we will ensure the transfer of the result to the component for its listing within our application:

import React, { useState } from 'react';
import CalculatorForm from './calculator/CalculatorForm';
import './App.css';

const App = () => {
    const [titleState] = useState('React kalkulačka');
    const [xState, setXState] = useState(0);
    const [yState, setyState] = useState(0);

    const inputXonchange = (event) => {
        console.log(event.target.value);
        setXState(event.target.value);
    };

    const inputYonchange = (event) => {
        console.log(event.target.value);
        setyState(event.target.value);
    };

    return (
        <div className="App">
            <h1>{titleState}</h1>
            <CalculatorForm
                xOnChange={inputXonchange}
                yOnChange={inputYonchange}
                x={xState}
                y={yState}
            />
        </div>
    );
};

export default App;

There should be nothing surprising here either:)

Styles

This completes the work on the components, but in the end, we will add CSS to make our application look at least a little.

src/App.css

Here, for simplicity, we will put style to a component of our application, because elsewhere there is basically nothing to style:

.App {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}

label {
  display: block;
  clear: both;
  margin: 10px 0;
}

label > input,
label > select {
  margin-left: 5px;
}

label > input {
  float: right;
}

input[type="submit"] {
  display: block;
  margin: 5px auto 0 auto;
}

.Result {
  font-size: 1.5em;
  font-weight: bold;
  margin: 10px 0;
}

As you can see within CSS, we will make nice use of the class names of our components.

And thus our work on the whole project is completed. If you look at the running application now, you should see a working calculator and you can try all the catches that come to mind. :)

React Basics

In JS, division by zero is possible and the result will be Infinity. However, we do not handle this "error" in the final application, but you have the space to do it yourself. You only need to check if the second number is zero when dividing. :) If we do not specify an operation and calculate the example, we also get an error.

If you do not understand anything, download the project from the attachment and go through it. We don't have that much code in the application, after a while you should in principle orient yourself.

The modules are not installed in the attachment due to their size. Therefore, you must use the npm install command before starting the application.

That's really all for this lesson.

OperationSelect.js

import React, { Component } from 'react';

export default class OperationSelect extends Component {
    constructor(props) {
        super(props);
        const value = this.props.value;
        this.state = { value: value ? value : '' };
        this.handleChange = this.handleChange.bind(this);
    }

    handleChange(event) {
        const value = event.target.value;
        this.setState({ value });
        this.props.onChange(value ? value : null);
    }

    render() {
        const { label, name, operations } = this.props;

        const options = Object.keys(operations).map((value, index) => {
            return <option key={index} value={value}>{operations[value]}</option>
        });

        return (
            <label htmlFor={name}>
                {label}
                <select id={name} required
                    value={this.state.value}
                    onChange={this.handleChange}>
                    <option value="">--Select an operation--</option>
                    {options}
                </select>
            </label>
        );
    }
}

Operation.js

const Operation = Object.freeze({
    ADD: 'add',
    SUBTRACT: 'subtract',
    MULTIPLY: 'multiply',
    DIVIDE: 'divide'
});

export default Operation;

CalculatorForm.js

import React, { Component } from 'react';
import NumberInput from './NumberInput';
import Operation from './Operation';
import OperationSelect from './OperationSelect';

export default class CalculatorForm extends Component {
    constructor(props) {
        super(props);
        this.operations = {
            [Operation.ADD]: 'Addition',
            [Operation.SUBTRACT]: 'Subtraction',
            [Operation.MULTIPLY]: 'Multiplication',
            [Operation.DIVIDE]: 'Division'
        };
        this.state = { x: 0, y: 0, operation: null, result: null };

        const handleChange = (name, value) => this.setState({ [name]: value });
        this.handleChangeX = handleChange.bind(this, 'x');
        this.handleChangeY = handleChange.bind(this, 'y');
        this.handleChangeOperation = handleChange.bind(this, 'operation');
        this.handleSubmit = this.handleSubmit.bind(this);
    }

    calculate() {
        const { x, y, operation } = this.state;
        switch (operation) {
            case Operation.ADD: return x + y;
            case Operation.SUBTRACT: return x - y;
            case Operation.MULTIPLY: return x * y;
            case Operation.DIVIDE: return x / y;
            default: return null; // It should never happen here.
        }
    }

    handleSubmit(event) {
        event.preventDefault();
        const result = this.calculate();
        this.setState({ result });
        this.props.onResultChange(result);
    }

    render() {
        return (
            <form className="CalculatorForm" onSubmit={this.handleSubmit}>
                <NumberInput name="x" label="First issue:"
                    value={this.state.x}
                    onChange={this.handleChangeX} />
                <NumberInput name="y" label="Second number:"
                    value={this.state.y}
                    onChange={this.handleChangeY} />
                <OperationSelect name="operation" label="Operation:"
                    operations={this.operations}
                    value={this.state.operation}
                    onChange={this.handleChangeOperation} />
                <input type="submit" value="Count" />
            </form>
        );
    }
}

App.js

import React, { Component } from 'react';
import CalculatorForm from './calculator/CalculatorForm';
import Result from './calculator/Result';
import './App.css';

export default class App extends Component {
    constructor(props) {
        super(props);
        this.state = { result: null };
        this.propagateResult = result => this.setState({ result });
    }

    render() {
        const title = 'React calculator';

        return (
            <div className="App">
                <h1>{title}</h1>
                <CalculatorForm onResultChange={this.propagateResult} />
                <Result value={this.state.result} />
            </div>
        );
    }
}

In the next lesson, AJAX in React, we'll review AJAX, show the API, and explain React Hook useEffect().


 

Did you have a problem with anything? Download the sample application below and compare it with your project, you will find the error easily.

Download

By downloading the following file, you agree to the license terms

Downloaded 15x (318.7 kB)
Application includes source codes

 

Previous article
React calculator components
All articles in this section
React Basics
Skip article
(not recommended)
AJAX in React
Article has been written for you by Vlasta
Avatar
User rating:
No one has rated this quite yet, be the first one!
Passionate reader, student, coder and writer.
Activities