새로운 비용 추가하기 버튼을 누르면 비용을 추가할 수 있는 폼이 뜨고, 폼에서 취소를 누르면 다시 폼은 없어지고 새로운 비용 추가하기 버튼이 생기도록 만들어 보자.
렌더링되는 상태가 바뀌는 것이기 때문에 상태를 추가한다. 디폴트 값으로는 false를 준다.
// 📍 NewExpense.js
const [isEditing, setIsEditing] = useState(false);
새로운 비용 추가 버튼을 만들고, 버튼을 클릭했을 때 상태가 변할 수 있도록 핸들러를 설정한다.
setIsEditing()
를 호출하는 startEditingHandler
를 생성한다.onClick
했을 때 startEditingHandler
를 포인팅한다.// 📍 NewExpense.js
const startEditingHandler = () => {
setIsEditing((prev) => !prev);
};
//...
return(
<div className="new-expense">
<button onClick={startEditingHandler}>새로운 비용 추가</button>
<ExpenseForm onSaveExpenseData={saveExpenseDataHandler} />
</div>
);
삼항연산자와 앤드연산자를 사용하여 isEditing의 true/false 값에 따른 jsx, <button>
혹은 <ExpenseForm />
를 반환한다.
!isEditing
이 true일 때)만 새로운 비용 추가 버튼을 렌더링한다.isEditing
이 true일 때)만 ExpenseForm 컴포넌트를 렌더링한다.// 📍 NewExpense.js
return (
<div className="new-expense">
{!isEditing && (
<button onClick={startEditingHandler}>새로운 비용 추가</button>
)}
{isEditing && (
<ExpenseForm
onSaveExpenseData={saveExpenseDataHandler}
/>
)}
</div>
);
취소버튼을 클릭하면 다시 isEditing의 상태를 바꾸도록 해야 하기 때문에 상태를 바꾸기 위한 stopEditingHandler
를 생성하여 <ExpenseForm />
컴포넌트에 props
으로 전달한다.
// 📍 NewExpense.js
const stopEditingHandler = () => {
setIsEditing((prev) => !prev);
};
//...
return (
<div className="new-expense">
{!isEditing && (
<button onClick={startEditingHandler}>새로운 비용 추가</button>
)}
{isEditing && (
<ExpenseForm
//🔥
onCancel={stopEditingHandler}
onSaveExpenseData={saveExpenseDataHandler}
/>
)}
</div>
);
ExpenseForm이 반환하는 jsx에 type="button"
인 버튼을 추가한다.
props로 받아온 onCancel을 onClick에 포인트한다.
//📍 NewExpense.js > ExpenseForm.js
import { useState } from "react";
import "./ExpenseForm.css";
const ExpenseForm = (props) => {
//...
return (
<form onSubmit={submitHandler}>
//...
<div className="new-expense__actions">
//🔥
<button type="button" onClick={props.onCancel}>
취소
</button>
<button type="submit">비용 추가</button>
</div>
</form>
);
};
export default ExpenseForm;
마지막으로 새로운 비용을 추가한 후, isEditing 상태를 변경하여 폼을 닫아보자.
// 📍 NewExpense.js
const saveExpenseDataHandler = (enteredExpenseData) => {
const expenseData = {
...enteredExpenseData,
id: Math.random().toString(),
};
//상위 컴포넌트에서 props으로 받은 함수에 저장할 data 인자로 전달
props.onAddExpenseData(expenseData);
//🔥 폼에 저장하고 나면 에디팅창 끄기
setIsEditing((prev) => !prev);
};
import "./NewExpense.css";
import ExpenseForm from "./ExpenseForm";
import { useState } from "react";
const NewExpense = (props) => {
const [isEditing, setIsEditing] = useState(false);
const saveExpenseDataHandler = (enteredExpenseData) => {
const expenseData = {
...enteredExpenseData,
id: Math.random().toString(),
};
//상위 컴포넌트에서 props으로 받은 함수에 저장할 data 인자로 전달
props.onAddExpenseData(expenseData);
//🔥 폼에 저장하고 나면 에디팅창 끄기
setIsEditing((prev) => !prev);
};
//🔥 새로운 비용 추가 버튼을 클릭했을 때 isEditing state를 변경하는 setIsEditing()를 호출한다.
const startEditingHandler = () => {
setIsEditing((prev) => !prev);
};
//🔥 취소 버튼을 클릭했을 때 isEditing state를 변경하는 setIsEditing()를 호출한다.
// 이 핸들러는 onCancel props을 통해 ExpenseForm으로 전달하여 거기서 포인팅하면 된다.
const stopEditingHandler = () => {
setIsEditing((prev) => !prev);
};
return (
<div className="new-expense">
//🔥 삼항연산자와 앤드연산자를 사용하여 isEditing의 true/false 값에 따른 jsx를 반환한다.
//isEditing 상태가 false일 때(즉, !isEditing가 true일 때)만 새로운 비용 추가 버튼을 렌더링한다.
{!isEditing && (
<button onClick={startEditingHandler}>새로운 비용 추가</button>
)}
//isEditing 상태가 true일 때(즉, isEditing가 true일 때)만 ExpenseForm 컴포넌트를 렌더링한다.
{isEditing && (
<ExpenseForm
onCancel={stopEditingHandler}
onSaveExpenseData={saveExpenseDataHandler}
/>
)}{" "}
</div>
);
};
export default NewExpense;
import { useState } from "react";
import "./ExpenseForm.css";
const ExpenseForm = (props) => {
//...
return (
<form onSubmit={submitHandler}>
//...
<div className="new-expense__actions">
//🔥
<button type="button" onClick={props.onCancel}>
취소
</button>
<button type="submit">비용 추가</button>
</div>
</form>
);
};
export default ExpenseForm;