Merhabalar.

Bu yazımda Calculator projemizi ele alacağız ve biraz refactor ve componentlere parçalama işlemi yapacağız. Bunu yaparken component’lerin birbiriyle nasıl iletişim kurduğunu göreceğiz.

Projemiz artık çok büyüdü ve bazı bölümler kendi başına birer component olacak seviyeye geldi.

O yüzden her birini farklı dosyalarda component’ler olarak tutacağız ve Calculator.jsx de bu componentleri çağıracağız.

Tabi çalışması gereken props bilgilerini geçerek component’lerin birbiriyle iletişim kurmasını sağlayacağız.

Tek tek dosyalarınızı oluşturun ve verdiğim kodlarla güncelleyin.

AlertMessage.jsx

Burada eski sayfamızda allState.alertMessage diye tanımladığım bir değişkenim vardı. Burada diyorum ki props bölümüne bakarsanız alertMessage olarak bunu alacağım ilgili yerinde böyle de kullanacağım.

const AlertMessage = ({alertMessage}) => {

    return (
        <div>
            {alertMessage ?

                <div className="header">
                    <div className="alert">{alertMessage}</div>
                </div> : null

            }
        </div>
    )
}

export default AlertMessage

Calculator.jsx İçine Ekleme

Burada her parçalama işlemi tamamlandıktan sonra Calculator.jsx dosyası içinde component’i çağırıp bizden istediği prop’u da verirsek, karıştırmadan işlemleri tamamlamış oluruz.

Bir tane örneğini göstereceğim daha sonrasında hepsini verip en sonda son halini paylaşacağım.

Sadece return içindeki güncellemeyi veriyorum. AlertMessage componentini çağırdım ve bizden alertMessage diye bir props göndermemizi bekliyordu ilgili değerini kendisine verdim ve gönderdim. Diğerleri de aynı bu mantıkta gerçekleştirilecek.

return (

        <div className="container">

            <AlertMessage alertMessage={allState.alertMessage} />

        </div>

)

History.jsx

Burada History component’inin içinde değişkenlerden ziyade, birde çalışması gereken bir fonksiyonumuz var.

Fonksiyonları da props geçerek yazıldığı component’ten çağırıp çalıştırabiliyoruz.

Bu işte component’lerin iletişimi olarak bahsettiğim konu.

handleDeleteHistory’i prop olarak alacağımız burada bildirdim. Calculator.jsx içinde History component’ini oluşturunca bu fonksiyonu props olarak göndereceğim.

import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faTrash } from '@fortawesome/free-solid-svg-icons';

const History = ({allState, history, handleDeleteHistory}) => {

    return (
        <div className={`historyContainer historyInfo ${allState.operationInfoOpen ? 'open' : ''}`}>

            <h4>History</h4>

            <div className={`operation-info info-container ${allState.operationInfoOpen ? 'open' : ''}`}>

                {history.map((process, index) => (
                    <div key={index} className="history-item">
                        <span>{process.count}: {process.num1} {process.operation} {process.num2} = {process.result} </span>
                        <FontAwesomeIcon icon={faTrash} onClick={() => handleDeleteHistory(index)} title="Delete" />

                    </div>
                ))}
            </div>
        </div>
    )
}

export default History

Process.jsx

ToggleButton Result ve Form ögelerini ortak bir component’te buluşturmak istedim.

Sonra bunlarında her birini birer component olarak ele alıp kendi dosyalarını oluşturdum.

İşte tam burada iki işlem birden var.

ToggleButton burada 2 tane props alıyor. Bu props’lar Calculator.jsx den Process’e geçirilmeli buradan da ToggleButton component’ine gönderilecek.

import React from 'react'
import ToggleButton from './ToggleButton'
import Result from './Result'
import Form from './Form'

const Process = ({allState, inputState, handleToggleOperationInfo, updateInputState, handleCalculate}) => {
    return (
        <div className="calculator">

            <ToggleButton allState={allState} handleToggleOperationInfo={handleToggleOperationInfo}  />

            <Result allState={allState} />

            <Form inputState={inputState} updateInputState={updateInputState} handleCalculate={handleCalculate} />

        </div>
    )
}

export default Process

ToggleButton.jsx

const ToggleButton = ({ allState, handleToggleOperationInfo }) => {
    return (
        <div className="toggle-container">

            <span className="mr">{allState.operationInfoOpen ? 'Close' : 'Open'} History</span>
            <button className={`toggle-button ${allState.operationInfoOpen ? 'active' : ''}`} onClick={handleToggleOperationInfo}>

            </button>

        </div>
    )
}

export default ToggleButton

Result.jsx

const Result = ({ allState }) => {
    return (
        <div className="result">
            <h1 id="result">{allState.result}</h1>
            <span>İşlem Adeti: {allState.count}</span>
        </div>
    )
}

export default Result

Form.jsx

const Form = ({inputState, updateInputState, handleCalculate}) => {
    return (
        <form onSubmit={handleCalculate}>
            <input type="number" name="num1" value={inputState.num1} onChange={updateInputState} required />
            <input type="number" name="num2" value={inputState.num2} onChange={updateInputState} required />
            <select name="operation" value={inputState.operation} onChange={updateInputState} required>
                <option value="">Select operation</option>
                <option value="add">Add</option>
                <option value="subtract">Subtract</option>
                <option value="multiply">Multiply</option>
                <option value="divide">Divide</option>
            </select>
            <button type="submit">Calculate</button>
        </form>
    )
}

export default Form

Tüm componentleri parçaladığımıza göre Calculator.jsx son kodlarına bakalım.

import "./Calculator.css";
import React, { useState } from 'react';
import AlertMessage from "./AlertMessage";
import Process from "./Process";
import History from "./History";

const Calculator = () => {

    // #region States

    const [inputState, setInputState] = useState({

        num1: 0,
        num2: 0,
        operation: '',
    });

    const [allState, setAllState] = useState({
        result: 0,
        count: 0,
        operationInfoOpen: false,
        alertMessage: '',
    });

    const [history, setHistory] = useState([]);

    // #endregion 

    // #region Functions

    const updateAlertMessage = (message) => {
        setAllState(prevState => ({
            ...prevState,
            alertMessage: message,
        }));
    }

    const updateResult = (result) => {

        setAllState(prevState => ({
            ...prevState,
            result: result,
            count: prevState.count + 1,
        }));

    }

    const updateOperationInfoOpen = (isOpen) => {

        setAllState(prevState => ({
            ...prevState,
            operationInfoOpen: isOpen,

        }));
    }

    const updateHistory = (operation, num1, num2, result) => {
        setHistory(prevList => [{ count: prevList.length + 1, operation, num1, num2, result }, ...prevList]);
    }

    const updateInputState = (event) => {

        const { name, value } = event.target;

        setInputState(prevState => ({
            ...prevState,
            [name]: value,
        }));
    }

    const initialInputState = () => {

        setInputState(prevState => ({

            num1: 0,
            num2: 0,
            operation: '',
        }));
    }

    const handleToggleOperationInfo = () => {

        if (history.length !== 0) {

            updateOperationInfoOpen(!allState.operationInfoOpen);
        }
        else {

            updateAlertMessage('There is no operation history');
            setTimeout(() => updateAlertMessage(''), 5000);
        }
    };

    const handleCalculate = (event) => {

        event.preventDefault();

        const form = event.target;
        const num1 = parseFloat(form.elements.num1.value);
        const num2 = parseFloat(form.elements.num2.value);
        const operation = form.elements.operation.value;

        let result;

        switch (operation) {
            case 'add':
                result = num1 + num2;
                updateHistory('+', num1, num2, result);
                break;
            case 'subtract':
                result = num1 - num2;
                updateHistory('-', num1, num2, result);
                break;
            case 'multiply':
                result = num1 * num2;
                updateHistory('*', num1, num2, result);
                break;
            case 'divide':
                if (num2 !== 0) {
                    result = num1 / num2;
                    updateHistory('/', num1, num2, result);
                } else {
                    updateAlertMessage('Cannot divide by zero');
                    setTimeout(() => updateAlertMessage(''), 5000);

                }
                break;
            default:
                updateAlertMessage('Invalid operation');
                setTimeout(() => updateAlertMessage(''), 5000);
        }

        updateResult(result);
        initialInputState();
    };

    const handleDeleteHistory = (index) => {
        setHistory(prevHistory => prevHistory.filter((item, i) => i !== index));

        if (history.length === 1) {
            updateOperationInfoOpen(false);
        }
    };

    // #endregion

    // #region Render

    return (

        <div className="container">

            <AlertMessage alertMessage={allState.alertMessage} />

            <History allState={allState} history={history} handleDeleteHistory={handleDeleteHistory} />

            <Process allState={allState} inputState={inputState} handleToggleOperationInfo={handleToggleOperationInfo} updateInputState={updateInputState} handleCalculate={handleCalculate} />

        </div>

    )

    // #endregion
}

export default Calculator;

Uygulama Testi

Hiçbir şeyin değişmeden çalışıyor olmasını bekleyeceğim.

Böylece projemizi derli toplu ayağı yere sağlam basar bir hale getirdik.

Bir sonraki yazımda görüşmek üzere.


Murat Bilginer
21 Şubat 1992'de doğdum. Endüstri Mühendisi olarak lisansımı 2016 yılında tamamladım. Industryolog Akademi - NGenius oluşumlarının kurucusuyum. Şu anda kendi şirketim Brainy Tech ile Web ve Mobil Geliştirme, AWS, Google Cloud Platform Sistemleri için DevOps, Big Data Analiz ve Görselleştirme hizmetleri sunmakta ve Online Eğitimler vermekteyiz.