자식 대 부모 컴포넌트 통신(상향식)

Yerim Son·2023년 6월 29일
0

React

목록 보기
12/23
post-thumbnail

+ 지난 이야기...
- ExpenseForm을 만들어 input값을 받고 저장해 NewExpense에 넘겨주었다.
- src-NewExpense-NewExpense.js & ExpenseForm.js가 있다.



ExpenseForm.js / 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;

  • NewExpense.js
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, dateExpenseItem component로 props를 이용해서 전달했던 것처럼.


단계

1단계: ExpenseForm component에서 수집한 expenseData를 NewExpense component에 전달. (NewExpense component에서 ExpenseForm을 사용하기 때문)
2단계: NewExpense component를 App component에 전달.


1단계: ExpenseForm(expenseData) -> NewExpense

1-1. ExpenseForm에 new 속성 설정.

//...
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 될 때.


1-2. saveExpenseDataHandler 추가

//...
// 매개변수(parameter)로 enteredExpenseData 설정
const saveExpenseDataHandler = (enteredExpenseData) => {
  // 객체 expenseData 추가
  const expenseData={ 
  ...enteredExpenseData, // ExpenseForm의 submitHandler에서 생성된 것. 모든 키:값 쌍을 가져와서 이 새로운 객체 expenseData에 추가함.
    
    id: Math.random().toString(), //새로운 key로 'id'를 추가. random숫자를 문자열로 변환하도록 설정.
  	
  }
  console.log(expenseData);
 }
//...

1-3. 함수 saveExpenseDataHandler를 ExpenseForm에서 사용하기

  • 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;
  • console.log()에 넘겨진 데이터가 찍힘

    (밑의 App.js에서 찍은 console.log는, 지금까지 한 방법과 동일한 방법으로 NewExpense -> App으로 데이터를 전달한 것이다.)


지금까지 한 게 lifting state up이네


와오 이번 강의는 아주 유용하고 꼬옥 알아야 하는 중요한 개념이 담긴 강의였다. 그래서 2-3번은 듣고 영자막 봤다가 한글자막 봤다가 하고 필기도 꼼꼼히 했다 🤓 유데미 62, 63강

0개의 댓글