앞서서 component에 대해 이야기할 때, props를 활용하여 component간의 데이터 송-수신을 다뤘었다.
이번에는 그 반대로 밑에서 위로 Bottom-Up approach에 대해서 이야기해보고자 한다.
Component communication in Bottom-Up approach(상향 접근 방식)
사실 이미 앞에서 다뤘던 부분이다.
const [enteredTitle, setEnteredTitle] = useState("");
const titleChangeHandler = (event) => {
setEnteredTitle(event.target.value);
}
<div className="new-expense__control">
<label>Title</label>
<input
type="text"
value={enteredTitle}
onChange={titleChangeHandler}
/>
</div>
해당 코드는 사용자의 입력을 수신하고 있으며, 사용자가 타이핑을 할 때마다 titleChangeHandler
함수가 실행된다.
이 과정에서 event 객체를 얻게 되는데 이것은 브라우저에서 기본으로 제공하는 객체이다.
사실 여기 있는 입력 요소인 <input>
을 component라고 생각할 수 있는데, 사용자 지정 component는 아니고 React에서 제공하는 내장 component로 DOM의 입력 요소로 해석된다.
하지만 결국 component의 특징을 가지며, onChange
속성을 포함하여 일부 props를 설정할 수 있다.
사실 이 onChange
속성도 특별한 것은 아니고 값으로 함수를 원하는 이름이 onChange
인 속성일 뿐이다.
그리고 여기에 event listener를 추가해준다.
그러면 React는 이 onChange
속성에 설정된 값을 보고 렌더링된 입력 요소에 해당 listener를 추가해주게 된다.
이러한 패턴은 사용자 지정 component에 충분히 복제할 수 있는 패턴이다.
자체 event 속성을 생성해 값을 호출하고, 부모 component로부터 자식 component로 함수를 전달할 수 있으며, 자식 component에서 그 함수를 호출할 수 있다.
그리고 그 함수가 호출되었을 때 parameter로 데이터를 전달받을 수 있다.
실제로 이런 경위로 자식에서 부모로 정보를 전달할 수 있다.
해당 데모 프로젝트에서는 index.js
-> App.js
-> NewExpense.js
-> ExpenseForm.js
의 계층 구조를 띈다.
const App = () => {
const addExpenseHandler = (expense) => {
console.log("In App.js");
console.log(expense);
};
return (
<div>
<NewExpense onAddExprese={addExpenseHandler} />
<Expenses items={expenses} />
</div>
);
};
const NewExpense = (props) => {
const saveExpenseDataHandler = (enteredExpenseData) => {
const expenseData = {
...enteredExpenseData,
id: Math.random().toString(),
};
console.log(expenseData);
props.onAddExprese(expenseData) ;
};
return (
<div className="new-expense">
<ExpenseForm onSaveExpenseData={saveExpenseDataHandler} />
</div>
);
};
const ExpenseForm = (props) => {
const [enteredTitle, setEnteredTitle] = useState("");
const [enteredAmount, setEnteredAmount] = useState("");
const [enteredDate, setEnteredDate] = useState("");
const titleChangeHandler = (event) => {
setEnteredTitle(event.target.value);
};
const amountChangeHandler = (event) => {
setEnteredAmount(event.target.value);
};
const dateChangeHandler = (event) => {
setEnteredDate(event.target.value);
};
const submitHandler = (event) => {
event.preventDefault();
const expenseData = {
title: enteredTitle,
amount: enteredAmount,
date: new Date(enteredDate),
};
props.onSaveExpenseData(expenseData);
setEnteredTitle("");
setEnteredAmount("");
setEnteredDate("");
};
return (
<form onSubmit={submitHandler}>
<div className="new-expense__controls">
<div className="new-expense__control">
<label>Title</label>
<input
type="text"
value={enteredTitle}
onChange={titleChangeHandler}
/>
</div>
<div className="new-expense__control">
<label>Amount</label>
<input
type="number"
value={enteredAmount}
min="0.01"
step="0.01"
onChange={amountChangeHandler}
/>
</div>
<div className="new-expense__control">
<label>Date</label>
<input
type="date"
value={enteredDate}
min="2019-01-01"
max="2023-12-31"
onChange={dateChangeHandler}
/>
</div>
</div>
<div className="new-expense__actions">
<button type="submit">Add Expense</button>
</div>
</form>
);
};
정확하게는 상위 component에서 props로 event handler 함수를 넘겨준다.
그렇게되면 하위 component에서는 onAddExprese, onSaveExpreseData
라는 props의 객체로 전달된다.
props의 객체 함수에 parameter로 전송해야하는 값을 넣어주게 되면 순차적으로 상위 component로 값이 올라간다.
이것이 bottom-up 방식의 component communication이다.