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