Hledáme nového kolegu do redakce - 100% home office, 100% flexibilní pracovní doba. Více informací.
Využij akce až 80 % zdarma při nákupu e-learningu - více informací. Zároveň pouze tento týden sleva až 80 % na e-learning týkající se Swift
discount week 80

Lekce 4 - Dokončení React kalkulačky

V minulé lekci, Komponenty React kalkulačky, jsme začali programovat React komponenty naší jednoduché kalkulačky. Tu jsme zvolili jako dostatečně jednoduchý projekt k vyzkoušení základů React.

V dnešním React tutoriálu dokončíme naši kalkulačku. :)

src/calculator/CalculatorForm.js

Zlatým hřebem bude samotná komponenta pro formulář kalkulačky, kde veškerou předchozí funkcionalitu propojíme a také zde definujeme naše výpočty.

V minulé lekci jsme si připravili konstru formuláře kalkulačky a obsluhu zadání hodnot operandů do číselných políček. Nyní si doplníme i funkci calculate(), kde vybereme operátor a rovnou provedeme výpočet. Skvěle se na to hodí použít switch. Celý kód včetně importů bude vypadat následovně:

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: '--Vyberte operaci--',
            label: '--Vyberte operaci--',
        },
    });
    const [resultState, setResultState] = useState(null);

    const options = [
        { value: 'ADD', label: 'Sčítání' },
        { value: 'SUBTRACT', label: 'Odčítání' },
        { value: 'MULTIPLY', label: 'Násobení' },
        { value: 'DIVIDE', label: 'Dělení' },
    ];

    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; // Sem by to nikdy nemělo dojít.
        }
    };

    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="První číslo:"
                    value={props.x}
                />
                <NumberInput
                    OnChange={props.yOnChange}
                    name="y"
                    label="Druhé číslo:"
                    value={props.y}
                />
                <Select
                    onChange={handleChange}
                    value={selectedOptionState.selectedOption}
                    options={options}
                />
                <input value="Spočítej" type="submit" />
            </form>
            <Result value={resultState} />
        </div>
    );
};

export default CalculatorForm;

Na první pohled to vypadá možná trochu komplikovaně, ale ve skutečnosti zde není nic, co bychom už neznali :) Za zmínku stojí, že ve funkci handleSubmit() používáme event.preventDefault(), čímž zabráníme znovunačtení stránky po odeslání uživatelského inputu. Abychom nemuseli znovu vynalézat kolo, importujeme si také Select z knihovny react-select. Díky tomuto doplňku z jedné z React knihoven bude aplikace působit hezky interaktivně.

src/App.js

Tento výukový obsah pomáhají rozvíjet následující firmy, které dost možná hledají právě tebe!

Nakonec ještě zajistíme předání výsledku do komponenty pro jeho výpis v rámci naší aplikace:

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;

Tady už by také nemělo být nic překvapivého :)

Styly

Tímto je práce na komponentách hotova, ale my si ještě na závěr doplňme CSS, aby naše aplikace alespoň trochu vypadala.

src/App.css

Zde pro jednoduchost dáme styl ke komponentě naší aplikace, protože jinde není v podstatě co stylizovat:

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

Jak vidíte v rámci CSS hezky využijeme názvy tříd našich komponent.

A tím je naše práce na celém projektu dokončena. Pokud se teď podíváte na spuštěnou aplikaci, měli byste vidět funkční kalkulačku a můžete na ní vyzkoušet všechny chytáky, které vás napadnou :)

V JS je možné dělení nulou a výsledek bude Infinity. Tento "error" však v konečné aplikaci neošetřujeme, ale máte prostor to udělat sami. Je třeba pouze při dělení zkontrolovat, zda druhé číslo je nula :) Pokud nezadáme operaci a příklad spočítáme, dostaneme také error.

Pokud vám není cokoli jasné, stáhněte si projekt z přílohy a projeďte si ho. Kódu v aplikaci tolik nemáme, po chvíli byste se v principu měli zorientovat.

V příloze nejsou nainstalované moduly, kvůli jejich velikosti. Je tedy potřeba před spuštěním aplikace použít příkaz npm install.

To je pro tuto lekci opravdu vše.

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="">--Vyberte operaci--</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]: 'Sčítání',
            [Operation.SUBTRACT]: 'Odčítání',
            [Operation.MULTIPLY]: 'Násobení',
            [Operation.DIVIDE]: 'Dělení'
        };
        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; // Sem by to nikdy nemělo dojít.
        }
    }

    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="První číslo:"
                    value={this.state.x}
                    onChange={this.handleChangeX} />
                <NumberInput name="y" label="Druhé číslo:"
                    value={this.state.y}
                    onChange={this.handleChangeY} />
                <OperationSelect name="operation" label="Operace:"
                    operations={this.operations}
                    value={this.state.operation}
                    onChange={this.handleChangeOperation} />
                <input type="submit" value="Spočítej" />
            </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 kalkulačka';

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

V příští lekci, AJAX v React, si zopakujeme práci s technologií AJAX, ukážeme si API a vysvětlíme si React Hook useEffect().


 

Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.

Stáhnout

Stažením následujícího souboru souhlasíš s licenčními podmínkami

Staženo 188x (320.29 kB)
Aplikace je včetně zdrojových kódů

 

Předchozí článek
Komponenty React kalkulačky
Všechny články v sekci
Základy React
Článek pro vás napsal Jindřich Máca
Avatar
Jak se ti líbí článek?
3 hlasů
Autor se věnuje převážně webovým technologiím, ale má velkou zálibu ve všem vědeckém, nejen ze světa IT. :-)
Aktivity

 

 

Komentáře

Avatar
Tomáš Chocenský:7.6.2019 20:25

Ahoj
Děkuji za skvělý úvod do Reactu. Chci se zeptat zda bude tato sekce pokračovat tvorbou webové aplikace k RESTful API v Node.js? Jak je slíbeno v poslední lekci o Node.js.
viz.: "Dalším krokem je postavit nad tím webovou aplikaci. Tutoriály k této problematice se objeví zde na síti v sekcích Angular a React."
Popřípadě kdy ji mohu očekávat?

S pozdravem
Tomáš

PS. předem děkuji za odpověď.

 
Odpovědět
7.6.2019 20:25
Avatar
mcihak
Člen
Avatar
mcihak:27.8.2019 21:33

Hezký úvod do Reactu. Pracuji s Reactem už více než dva roky a tak jsem si říkal, že určitě v článku najdu nějaké nedostatky. Ale po přečtení prvních 4 lekcí musím přiznat, že jsem žádné významné neobjevil :) Určitě přeču i další díly, jsem zvědavý jak se autor "popere" s pokročilejšími tématy (např. lifecycle metody).

 
Odpovědět
27.8.2019 21:33
Avatar
Peter Mlich
Člen
Avatar
Peter Mlich:15. dubna 10:39

Mne tam treba neni uplne jasne, proc tam generujes tisice handlu a elementu. Ja ted resim takovou slozitejsi vec, neco jako anketa, hodnoceni 5 hvezdickami, ale mam to tam 10x. To si neumim predstavit kod, kdybych to resil stejne jako ty.

    handleChange(event) {
        const value = event.target.value;
//        this.setState({ value });
//        this.props.onChange(value ? value : null);
// tady volam nejakou funkci update(value) // kde value je pole, napr {id:2,value:5}
    }
...
for (var i=0; i<level; i++)
{
actived = i<data.value;
value = JSON.stringify({id:id, value:i}); // id je id cele skupiny
if (actived)
{
point_bar.push(<input type="radio" onClick={this.handleChange} value={value} checked/>);
}
else    {
point_bar.push(<input type="radio" onClick={this.attPointBarClick} value={value}/>);
}

Jedna se o nastaveni nekolika parametru postavy, sila, obratnost, inteligence... Pouzivam radio buttony, v budoucnu image. Nejspis budu muset pouzit image button, protoze, jinak se parametr value neprenasi do handle, treba ze spanu.
No, jeste presne nevim, zatim je to spis takovy prvni pokus.

Editováno 15. dubna 10:42
 
Odpovědět
15. dubna 10:39
Avatar
Peter Mlich
Člen
Avatar
Peter Mlich:15. dubna 11:05

Mozna by se mi ale vic siklo predavat odkaz na prvek. To zatim uplne nevim, jak to resit. Nekde jsem videl zapis

self = this;
<span onchange={self.nejakyHandleClick}>

//nebo
<input ... ref={this.input}>

Ale, neumi s tim pracovat, takze je to pro mne bezcenne.
Mozna by bylo lepsi tu kalkulacku prave predelat jinak, aby stacil 1 onclick, kteremu predas treba pole nebo si je veme z toho elementu. Protoze, takhle, pro kazde tlacitko, extra, musis pridavat handleClick. V JS bys to preci normalne resil takto:

<script>function handleClick(x) {...}</script>
<input onclick="handleClick(this)" value="{1,2,3}"> // alert(x.value)
<span onclick="handleClick({1,2,3})">text</span> // alert(x)
 
Odpovědět
15. dubna 11:05
Tento výukový obsah pomáhají rozvíjet následující firmy, které dost možná hledají právě tebe!
Avatar
Kája Matějková:17. května 12:05

Ahoj, moc děkuji za úvod do Reactu. Je to hezky vysvětlené:)

 
Odpovědět
17. května 12:05
Avatar
Karel Karafiát:12. října 14:41

Ahoj, jak je v kódu použitý soubor Operation.js uvedený v úvodu této lekce? Nějak nemohu najít jeho vazbu.

 
Odpovědět
12. října 14:41
Avatar
Samuel Hél
Tým ITnetwork
Avatar
Odpovídá na Karel Karafiát
Samuel Hél:13. října 10:14

Ahoj, šlo o pozůstatek z předchozí verze React, děkuji za upozornění :) Soubor můžeš odstranit a nebo konstanty ze souboru použít namísto textů 'ADD', 'MULTIPLY' atd.

 
Odpovědět
13. října 10:14
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 7 zpráv z 7.