Lesson 3 - React calculator components
In the last lesson, First application in React, we started creating a simple calculator using the React library.
Today we will add properties and states to the calculator component.
Components
We begin concept properties that are used for configuration and data transfer into components.
Properties
We simply write an attribute to the component within JSX, eg
x="5"
, and this component will then make it available internally
via props
, ie props.x
. In short, it will be
5
. Let's take an example.
src/calculator/Result.js
The use of a property (props) is beautifully suited for our result display component. As a parameter, we pass the current value to be displayed:
import React from 'react'; const Result = (props) => { const result = props.value; if (result || result === 0) return <div className="Result">Result: {result}</div>; else return null; }; export default Result;
There are several things that happen in this code. We pass the value of the
displayed result using the value
property and store it in the local
result
constant. We then work with it and, according to its value,
check whether we should display our result or not.
It's a good idea to point out that if a component has nothing to
render, it should return null
. It follows from the functional
paradigm - a function must always return a value.
Next, we can try to use the property. Simply set inside the
CalculatorForm
component, for example:
<Result value="5" />
and we should immediately see the result
in the running application:)
src/calculator/NumberInput.js
Before we illustrate how states and events work, we will prepare the
NumberInput
component:
import React from 'react'; let NumberInput = (props) => { const { name, label } = props; return ( <label htmlFor={name}> {label} <input onChange={props.OnChange} id={name} type="number" name={name} required /> </label> ); }; export default NumberInput;
As we can see, we added the properties of the name
and
label
components, but we have to pass them in the parameters of the
function (they are always called props
). It is smarter to see that
we have also prepared an onChange
event, which we will also pass to
the properties of the component and we will work with it in another
component.
And now to the states and the processing of events.
State
State (state) is similar characteristics, but is only used inside the components for control of data flow. In essence, this is the component's private data for internal use only. Let's show it again with an example.
The React functional uses the useState
library to manage states.
In order to use useState()
, we need to import this library. Set an
empty string ("")
or 0
as the initial value. Let's not
be afraid that our IDE will underline setState
and announce that we
don't use it anywhere. We'll get to that:-)
src/calculator/CalculatorForm.js
We will create an array that will contain two variables - one is the initial
state, the other will be stored later. setResult
possible code, it
is good to name these variables, for example here result
and
setResult or even better resultState
and
setResultState
.
We pass the original state to the useState()
function
resultState
variable. This can be an empty string
useState("")
, null
or any value. We then change the
value of the variable using setResultState
, most often we do it
when processing events (the value changes after the user clicks
on something):
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 operation--', label: '--Select operation--', }, }); const [resultState, setResultState] = useState(null); const options = [ { value: 'ADD', label: 'Add' }, { value: 'SUBTRACT', label: 'Subtract' }, { value: 'MULTIPLY', label: 'Multiply' }, { value: 'DIVIDE', label: 'Divide' }, ]; const handleSubmit = (event) => { event.preventDefault(); //const result = calculate(); //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.value} options={options} /> <input value="Count" type="submit" /> </form> <Result value={resultState} /> </div> ); }; export default CalculatorForm;
We see that the handleChange()
and handleSubmit()
functions are used to store the new value of the variable and other actions that
the application will perform after calling this function. In our case, it will
later be the calculate()
function, which will take care of the very
essence of the calculator, ie the use of mathematical operations. So far, we
have handleSubmit()
function so that we can compile the
calculator.
Great, we were able to synchronize our state with the value in the
<input>
element. Now, if we write something in it, the
handleChange()
method is called and the internal state of the
component is updated. Note also that we have our own event for both numeric
inputs, ie xOnChange
and yOnChange
.
Finally, here we promote our state to the outside world. As we have already
explained, the state is only for our internal needs, so if we want to manifest
it externally, we will arrange it by promoting it through properties. In our
case, we are only promoting the event to our props.onChange
property with a slight improvement in number conversion. So the one who will use
our program will always get a numeric value (or NaN
), as we would
probably expect from a numerical input
So far we have an incomplete calculator which we will finish in the next lesson.
Result.js
import React, { Component } from 'react'; export default class Result extends Component { render() { const result = this.props.value; if (result || result === 0) return <div className="Result">Result: {result}</div>; else return null; } }
NumberInput.js
import React, { Component } from 'react'; export default class NumberInput extends Component { constructor(props) { super(props); this.state = { value: this.props.value }; this.handleChange = this.handleChange.bind(this); } handleChange(event) { const value = event.target.value; this.setState({ value }); this.props.onChange(Number(value)); } render() { const { name, label } = this.props; return ( <label htmlFor={name}> {label} <input id={name} type="number" name={name} required value={this.state.value} onChange={this.handleChange} /> </label> ); } }
CalculatorForm.js
export default class CalculatorForm extends Component { constructor(props) { super(props); 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'); } render() { return ( <form className="CalculatorForm"> <NumberInput name="x" value={this.state.x} onChange={this.handleChangeX} /> <NumberInput name="y" value={this.state.y} onChange={this.handleChangeY} /> <OperationSelect /> <input type="submit" value="Count" /> </form> ); } }
In the next lesson, Completing the React Calculator, we'll finish the calculator in React.