Önemli: Diğer yazılarımla direkt bağlantılı bir yazıdır. İlk olarak bu yazıdan okumaya başladıysanız, eğitim serisine kısaca bir göz atmanızı tavsiye ederim.
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.