+ 지난 이야기...
-
ExpenseForm을 만들어 input값을 받고 저장해 NewExpense에 넘겨주었다.
-
src-NewExpense-NewExpense.js & ExpenseForm.js가 있다.
import React, { useState } from "react";
import "./ExpenseForm.css";
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);
};
// submitHandler (폼 입력 처리)
const submitHandler = (event) => {
event.preventDefault(); // 서버에 기본 요청이 보내지는 것을 막음. 요고 안하면 페이지가 리로드됨.
// 입력된 모든 데이터 결합. 폼이 제출됐을 때 객체를 생성.
const expenseData = {
title: enteredTitle,
amount: enteredAmount,
date: new Date(enteredDate), // 문자열로 되어있는 날짜를 날짜 객체로 변환해서 전달.
};
// console.log(expenseData);
props.onSaveExpenseData(expenseData);
// 폼 전송 후 입력값 초기화 (value 초기화)
setEnteredTitle("");
setEnteredAmount("");
setEnteredDate("");
};
return (
<form onSubmit={submitHandler}>
<div className="new-expense__controls">
<div className="new-expense__control">
<label>Title</label>
<input
type="text"
onChange={titleChangeHandler}
value={enteredTitle}
/>
</div>
<div className="new-expense__control">
<label>Amount</label>
<input
type="number"
min="0.01"
step="0.01"
onChange={amountChangeHandler}
value={enteredAmount}
/>
</div>
<div className="new-expense__control">
<label>Date</label>
<input
type="date"
min="2019-01-01"
max="2023-12-31"
onChange={dateChangeHandler}
value={enteredDate}
/>
</div>
</div>
<div className="new-expense__actions">
<button type="submit">Add expense</button>
</div>
</form>
);
};
export default ExpenseForm;
import React from "react";
import "./NewExpense.css";
import ExpenseForm from "./ExpenseForm";
const NewExpense = (props) => {
// enteredExpenseData: ExpenseForm에서 들어온 새로운 expenseData
const saveExpenseDataHandler = (enteredExpenseData) => {
const expenseData = {
...enteredExpenseData,
id: Math.random().toString(),
};
console.log(expenseData);
props.onAddExpense(expenseData);
};
return (
<div className="new-expense">
<ExpenseForm onSaveExpenseData={saveExpenseDataHandler} />
</div>
);
};
export default NewExpense;
- ExpenseForm component에서는 data가 필요 없다.
- NewExpense, 정확하게는 App.js component에 data가 필요하다. 왜냐하면 거기에 expenses array가 있고, 사용자가 입력한 new expense를 기존의 array에 추가하는 게(id까지 추가하여) 궁극적 목표이기 떄문이다.
- ExpenseForm component data -> App component (자식 -> 부모)
지금까지는 부모 -> 자식, 위 -> 아래로 데이터를 넘겨주는 방법만 배웠다.
예를 들어, Expense
component로부터 title, amount, date
를 ExpenseItem
component로 props
를 이용해서 전달했던 것처럼.
단계
1단계: ExpenseForm component에서 수집한 expenseData를 NewExpense component에 전달. (NewExpense component에서 ExpenseForm을 사용하기 때문)
2단계: NewExpense component를 App component에 전달.
//...
const NewExpense = () => {
//...
return(
<div className='new-expense'>
<ExpenseForm onSaveExpenseData={saveExpenseDataHandler} />
// 함수 실행시키지 않고 point만. 그래서 이 함수 자체가 ExpenseForm으로 전달됨.
</div>
);
};
//...
NewExpense에 import된 ExpenseForm에 새로운 속성 추가함.
나는 onSaveExpenseData라고 이름을 설정했다.
이 속성에 대한 함수는 NewExpense component 안에 정의되어야 한다.
그리고 값으로 함수 saveExpenseDataHandler를 전달함. 바로 밑에 이 함수가 나온다.
💡 naming: 관례에 따라 on으로 시작함. ex) onSaveExpenseData -> form이 submitted 될 때.
//...
// 매개변수(parameter)로 enteredExpenseData 설정
const saveExpenseDataHandler = (enteredExpenseData) => {
// 객체 expenseData 추가
const expenseData={
...enteredExpenseData, // ExpenseForm의 submitHandler에서 생성된 것. 모든 키:값 쌍을 가져와서 이 새로운 객체 expenseData에 추가함.
id: Math.random().toString(), //새로운 key로 'id'를 추가. random숫자를 문자열로 변환하도록 설정.
}
console.log(expenseData);
}
//...
ExpenseForm component를 사용할 때 설정했던 onSaveExpenseData속성을 전달해서 값(여기서는 saveExpenseDataHandler)을 추출할 수 있다.
ExpenseForm에서 props를 받아서, submitHandler에서 props.onSaveExpenseData로 접근해서 함수를 실행한다. (onSaveExpenseData 키에서 얻은 값이 함수라 실행할 수 있음)
정리: saveExpenseDataHandler함수는 NewExpense component에서 정의되었지만, ExpenseForm에 속성 키:값으로 함수를 넘겨주고, 그 함수는 ExpenseForm에서 onSaveExpenseData prop을 받아 props.키로 실행한다.
We can call a function in the new expense component and we can pass data as a parameter.
// ...
const ExpenseForm = (props) => {
//...
const submitHandler = (event) => {
event.preventDefault();
// 입력된 모든 데이터 결합. 폼이 제출됐을 때 객체를 생성.
const expenseData = {
title: enteredTitle,
amount: enteredAmount,
date: new Date(enteredDate),
};
// console.log(expenseData);
props.onSaveExpenseData(expenseData);
//NewExpense에서 onSaveExpenseData를 호출할 때, expenseData를 argument로써 전달할 수 있다.
//그리고 이 값이 NewExpense에서 매개변수로 받는 값임.
// 호출하고 있는 함수에 데이터 전달!!!!! lifting state up
// 폼 전송 후 입력값 초기화 (value 초기화)
setEnteredTitle("");
setEnteredAmount("");
setEnteredDate("");
};
return (
<form onSubmit={submitHandler}>
<div className="new-expense__controls">
<div className="new-expense__control">
<label>Title</label>
<input
type="text"
onChange={titleChangeHandler}
value={enteredTitle}
/>
</div>
<div className="new-expense__control">
<label>Amount</label>
<input
type="number"
min="0.01"
step="0.01"
onChange={amountChangeHandler}
value={enteredAmount}
/>
</div>
<div className="new-expense__control">
<label>Date</label>
<input
type="date"
min="2019-01-01"
max="2023-12-31"
onChange={dateChangeHandler}
value={enteredDate}
/>
</div>
</div>
<div className="new-expense__actions">
<button type="submit">Add expense</button>
</div>
</form>
);
};
export default ExpenseForm;
지금까지 한 게 lifting state up이네