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.
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