Merhabalar.

Bir yazımda React’te Two-Way Binding olayını inceleyeceğiz.

Bir önceki yazımdan hatırlayacağınız üzere Calculator uygulamamız vardı. Bu konuyu yine Calculator üzerinden sizlere anlatmaya çalışacağım.

Biz ekranda sayılar girip bir işlem yaptırdığımızda, onSubmit olayında preventDefault() kullandığımız için sayfa yenilenmez ve Inputbox’lara yazdığımız değerler aynen kalır. Ama bazı durumlarda biz bu değerler işlem sonrasında sıfırlansın isteriz. İşte bu işlemi yapmak için kullanacağımız yönteme Two-Way Binding yöntemi diyoruz.

Burada yapmamız gereken işlem şu.

Inputbox’ların value property’lerini dinamik yönetmek. Her Inputbox ve Dropdownbox için birer state değişkeni tanımlayacağız. Sonrasında bu input’ların onChange event’lerine değişim değerlerini set edeceğiz.

Tüm işlem bittiğinde bu değerlerin içini boşaltacağız. Böylece alanlar temizlenmiş olacak.

Kodlamaya geçebiliriz.

InputState’inin Eklenmesi

State olarak aşağıdaki kodu ekliyoruz.

const [inputState, setInputState] = useState({

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

InitialInputState Fonksiyonunun Yazılması

Bu değişkenleri başlangıç değerlerine set edecek bir initialInputState fonksiyonu yazıyorum.

Bu fonksiyon sayesinde Two-Way Binding işlemini gerçekleştireceğim.

const initialInputState = () => {

        setInputState(prevState => ({

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

UpdateInput Fonksiyonunun Yazılması

InputState’leri, input’lardaki değerlere göre güncelleyecek update fonksiyonunu yazalım.

const updateInputState = (event) => {

        const { name, value } = event.target;

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

Update işlemi gerçekleştirirken değerler bir input’tan geleceği için ve bu fonksiyon input’ların onChange event’ine bağlanacağı için, event parametresi ile elementin kendisini yakalayabiliyordum.

Sonra name, value değişkenlerini, destructuring yöntemiyle targettan çekiyorum.

Bu sayede şu gerçekleşiyor gelen input’un name’i “num1” key olarak name değişkenine gidiyor, value değeri de value’ya gidiyor.

Sonra yaptığımız işlem eski değerleri al ve sana gönderilen name ile bir key yarat ve değeri kendisine ata ve güncelle. Böylece her input için farklı fonksiyonlar yazmaktan kurtulmuş olduk.

Form Elemanlarını Güncelle

Şimdi form ögeleri üzerinde yaptığımız güncellemelere bakalım.

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

Input’lara value property’leri eklendi ve state’lerden güncellenecek şekilde ayarlandı.

onChange event’lerine update fonksiyonu bağlandı.

InitialInputState Fonksiyonunu Çalıştır – Two-Way Binding Gerçekleşsin

Artık son olarak Calculate işleminin sonunda initial fonksiyonunun çağrılması kaldı.

initialInputState();

İşte istediğimiz şeyi elde edebilmiş olduk.

Uygulama Testi

Şimdi testlerimize bakalım.

İki işlem gerçekleştirdim. İşlemlerim sonrasında input alanlarım sıfırlandı.

Son olarak kodların tamamını paylaşayım. İnceleyebilmeniz adına.

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

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();
    };

    // #endregion

    // #region Render

    return (

        <div className="app">

            {allState.alertMessage ?

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

            }

            <div className={`operation-info info-container ${allState.operationInfoOpen ? 'open' : ''}`}>
                <h4>Operation History</h4>
                {history.map((process, index) => (
                    <p key={index}>{process.count}: {process.num1} {process.operation} {process.num2} = {process.result}</p>
                ))}
            </div>

            <div className="toggle-container">

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

                </button>

            </div>

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

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

        </div>


    )

    // #endregion
}

export default Calculator;

Bu olaya two-way binding diyorduk örneğiyle öğrenmiş olduk.

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.