JiSoo's Devlog

[Udemy React 완벽 가이드] Section 4 본문

Frontend/React

[Udemy React 완벽 가이드] Section 4

지숭숭숭 2024. 1. 4. 11:32

이벤트 리스닝 및 이벤트 핸들러 수행하기

  • 모든 이벤트 핸들러 props는 값으로 함수가 필요하다
  • onClick과 다른 on props에 대한 값으로 전달된 함수는 이벤트가 발생했을 때 실행되어야 한다
<button onClick={() => {console.log("Clicked!");}}>Change Title</button>
 
리액트 요소에 이벤트 리스너를 간단히 추가하는 방법

 

 

const ExpenseItem = (props) => {
  const clickedHandler = () => {
    console.log("Clicked!!!!");
  };

  return (
    <Card className="expense-item">
      <ExpenseDate date={props.date} />
      <div className="expense-item__description">
        <h2>{props.title}</h2>
        <div className="expense-item__price">${props.amount}</div>
      </div>
      <button onClick={clickedHandler}>Change Title</button>
    </Card>
  );
};
 
  • 함수 따로 지정해서 onClick={clickedHandler}로 지정만 해줘도 됨
  • 일부 이벤트는 특정한 요소에서만 사용 가능하지만 그건 모두 기본적인 DOM의 동작에 기반한다
  • 만약 요소가 이벤트를 지원한다면 on 다음에 이벤트명 props를 추가해서 리액트로 리스너를 추가할 수 있다
  • 이벤트명은 대문자로 시작(onClick)
  • 함수의 명명은 함수가 이벤트에 의해 트리거된다면 이름 뒤에 Handler를 붙이는 게 좋

 

컴포넌트 함수가 실행되는 방법

  • let으로 변수 선언해 주고 처음에는 props.title로 주고 title 변수를 JSX 코드에서 사용해 품명을 아웃풋 한다
const ExpenseItem = (props) => {
  let title = props.title;

  const clickedHandler = () => {
    console.log("Clicked!!!!");
  };

  return (
    <Card className="expense-item">
      <ExpenseDate date={props.date} />
      <div className="expense-item__description">
        <h2>{title}</h2>
        <div className="expense-item__price">${props.amount}</div>
      </div>
      <button onClick={clickedHandler}>Change Title</button>
    </Card>
  );
};
 
  • 이렇게 해준 이유는 title이 변수이기 때문에 clickHandler가 실행될 때마다 바꿀 수가 있다
  • 리액트는 JSX 코드에서 마주치는 모든 컴포넌트 함수를 계속해서 호출하고 이 함수들이 리턴했을 수도 있는 모든 함수를 호출한다
  • 리액트는 앱이 처음 렌더링 될 때 모든 과정을 한 번 거치지만 그리고 끝이다 하지만 오늘날의 앱은 화면에 표시되는 내용을 업데이트해야 하는 게 당연하다
  • 그렇게 때문에 우리에겐 리액트에 변경된 사항이 있으며 특정 컴포넌트가 재평가되어야 한다는 사실을 말해줄 방법이 필요하다

 

 

state와 함께 일하기

const clickedHandler = () => {
    title = "Updated!";
    console.log(title);
  };
 
  • 여기서 바뀌는 title은 데이터
  • 이 컴포넌트가 재평가되고 이 title 데이터에 변동 사항이 생기면 화면에도 반영되어야 한다
  • 기본적으로 일반 변수들은 재평가를 트리거 하지 않는다
  • 변수가 바뀐 걸로 함수로 다시 실행하지 않는다
  • 리액트에 재실행하라고 알려주려면 리액트 라이브러리에서 어떤 것을 가져오기 해야 한다 -> named import (지정해서 가져오기)
  • 중괄호를 추가하고 리액트 라이브러리에서 추가하고 싶은 것을 지정하면 된다
import React, { useState } from "react";
 
  • useState는 리액트 라이브러리가 제공하는 것으로 값을 상태로 정의할 수 있도록 해주어 값에 변화가 생기면 다시 호출하는 컴포넌트 함수에 반영이 된다
  • useState는 리액트 훅이라고 부르는 것인데 모든 리액트 훅은 이름이 'use'로 시작한다
  • 모든 훅은 컴포넌트 함수 안에서 직접 호출돼야 하고 중첩 함수 안에서 호출될 수 없다
  • useState는 기본 상태 값을 원한다
useState(props.title);  // 초깃값을 인자로
 
  • useState는 단지 리턴만 하는 것이 아니라 해당 변수에 새 값을 할당하기 위해 호출할 수 있는 함수를 리턴해준다 -> 상태 변수가 작동하는 방식
  • 배열 구조 할당 - 두 요소를 각각의 변수 또는 상수에 저장한다, 순서가 중요하다
const [title, setTitle] = useState(props.title);
 
  • 첫 번째 값은 관리되는 해당 값에 대한 포인터, 현재 상태 값
  • 처음에는 props.title, 즉 props.title에 저장된 값
  • 두 번째 요소는 나중에 새 품명을 설정할 때 호출할 함수
  • useState는 늘 두 요소로 구성된 배열을 리턴한다
  • 첫 번째 값은 늘 현재의 상태 값이며 두 번째 요소는 그것을 업데이트하는 함수이다
  const clickedHandler = () => {
    setTitle("Updated"); //상태 업데이트 함수 호출
    console.log(title);
  };
 
  • 상태 업데이트 함수를 호출할 때마다 이 특별한 변수는 새 값을 수신할 뿐만 아니라 상태 업데이트 함수를 호출한 setTitle(), 상태를 초기화한 컴포넌트 함수 useState(props.title)를 재실행한다
  • 데이터 변화가 사용자 인터페이스에 반영되어야 한다면 상태가 필요, 일반 변수로는 반영X
 

 

"상태" 다루기

  • 버튼 클릭하면 가격이 $100에서 $75로 변경되어야 함
import React , { useState } from 'react';

import './styles.css';

export default function App(){
    
    const [price,setPrice] = useState(100) 
    
    const clickedHandler = () => {
    setPrice(75);
  };
  
    return (
        <div>
            <p>${price}</p>
            <button onClick={clickedHandler}>Apply Discount</button>
        </div>
    );
}
 

 

 

useState Hook 자세히 살펴보기

  • useState는 특정 컴포넌트 인스턴스에 대한 상태를 등록한다
  • ExpenseItem을 호출할 때마다 새로운 상태가 생성되는데 생성되는 방식은 같지만 리액트는 각각을 독립적으로 관리한다 -> 각각 자체적인 상태가 있다 => 컴포넌트 인스턴스 단위 기반!
  • 상태는 컴포넌트 인스턴스 별로 구별된다
  • 상태가 업데이트되면 업데이트 함수를 호출한다
  • 상태가 업데이트되면 컴포넌트 함수도 재실행된
  • 등호를 이용해 값을 할당하지 않고 setTitle('Updated') 이런식으로 상수 사용
  • 컴포넌트 함수가 재실행될 때마다 새로운 상태의 스냅샷을 얻는 것이다
  • 애플리케이션에 반응성을 부여하는 건 상태이다
  • 상태가 없다면 사용자 인터페이스는 그대로일 것이다
  • 상태가 있고 이벤트를 수신할 수 있다면 사용자 인풋에 반응할 수 있고 인풋에 따라 화면에 보이는 내용도 바뀔 수 있다
  • useState로 상태를 등록해주고 값은 두 개 리턴(값 자체, 업데이트 함수)하고 상태가 변해야 할 때마다 업데이트 함수 호

 

 

양식 입력 추가하기

  • 사용자가 비용 데이터를 입력할 수 있는 폼 렌더링
  • 폼도 별도의 컴포넌트도 분리해 주는데 폼의 로직은 전용 컴포넌트에서 작동한다
  • 리액트에서 input 태그는 시작 태그와 종료 태그 사이에 아무 내용이 없기 때문에 자체 closing 태그로 써줘야 한다
  • input type="date"에서 min과 max 시작과 종료 날짜, 이를 추가해 주는 이유는 추후에 연도별로 제공하는 필터 추가할 계획이기 때문에

 

NewExpense.js

import React from "react";
import "./NewExpense.css";
import ExpenseForm from "./ExpenseForm";

const NewExpense = () => {
  return (
    <div className="new-expense">
      <ExpenseForm />
    </div>
  );
};

export default NewExpense;
 

ExpenseForm.js

import React from "react";
import "./ExpenseForm.css";

const ExpenseForm = () => {
  return (
    <form>
      <div className="new-expense__controls">
        <div className="new-expense__control">
          <label>Title</label>
          <input type="text" />
        </div>
        <div className="new-expense__control">
          <label>Amount</label>
          <input type="number" min="0.01" step="0.01" />
        </div>
        <div className="new-expense__control">
          <label>Date</label>
          <input type="date" min="2019-01-01" max="2022-12-31" />
        </div>
      </div>
      <div className="new-expense__actions">
        <button type="submit">Add Expense</button>
      </div>
    </form>
  );
};

export default ExpenseForm;
 

App.js

import React from "react";
import Expenses from "./components/Expenses/Expenses";
import NewExpense from "./components/NewExpense/NewExpense";

const App = () => {
  const expenses = [
    {
      id: "e1",
      title: "Toilet Paper",
      amount: 94.12,
      date: new Date(2020, 7, 14),
    },
    { id: "e2", title: "New TV", amount: 799.49, date: new Date(2021, 2, 12) },
    {
      id: "e3",
      title: "Car Insurance",
      amount: 294.67,
      date: new Date(2021, 2, 28),
    },
    {
      id: "e4",
      title: "New Desk (Wooden)",
      amount: 450,
      date: new Date(2021, 5, 12),
    },
  ];
  return (
    <div>
      <NewExpense />
      <Expenses items={expenses} />
    </div>
  );
};

export default App;
 
 

 

사용자 입력 수신하기

  • onChange는 모든 input 타입에 같은 이벤트를 사용할 수 있다
 

.

  • 여기서 함수 실행하는 게 아니기 때문에 함수를 지목만!
  • 지정해 준 함수에서도 기본 자바스크립트 문법과 같이 event 객체를 자동으로 얻을 수 있다
  • 기본적으로 onChange 프로퍼티를 통해 리액트에 이 함수를 입력했기 때문에 리액트 또는 브라우저 자체에서 change 이벤트가 발생할 때 event 객체를 얻도록 해줄 것이다
 
  • value 프로퍼티에 이벤트가 발생한 시점에 입력되어 있는 값이 들어 있다
const titleChangeHandler = (event) => {
    console.log(event.target.value);
  };
 

 

 

여러 상태 사용하기

  • 값을 저장하고 함수가 재실행 되어도 컴포넌트가 재평가되어도 값이 남아 있도록 하기 위해서 상태를 활용할 것
const [enteredTitle, setEnteredTitle] = useState(''); 
//괄호 안에 input에 대한 상태 설정, 처음에는 빈 문자열-> 이 컴포넌트가 처음 렌더링 될 때는 아직 아무것도 입력되지 않을 거기 때문
//구조분해할당을 활용해 두 개의 요소를 얻는다
 
  • 사용자가 무언가를 입력하면서 발생하는 이벤트에 반응할 때 setEnteredTitle을 호출해 event.target.value 즉, 현재 입력된 값을 매개변수로 setEnteredTitle의 인자로 입력할 수 있다 -> 그러면 상태에 저장될 것이다
  • 나중에 컴포넌트를 업데이트하고 다시 렌더링 하는 데 어차피 상태가 필요할 것이기 때문에 이점이 있다
  • 같은 컴포넌트 안에 있는 여러 개의 상태는 완전히 구별된다
  • useState( ) -> 괄호 안에는 문자열로 받아서 문자열로 상태를 초기화
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);
  };
 

 

 

상태 하나 사용하기

  • 여러 개의 상태 슬라이드를 사용해도 된다
  • 세 상태 모두 같은 폼에 연결되어 있으며 세 상태 모두 같은 사용자 인풋을 저장하므로 기본적으로 같은 개념이 세 번 반복되는 것이다
  • useState( ) 값으로 객체를 입력하면 되는데 문자열이나 숫자가 아니라 객체라는 것
  • useState를 한 번만 호출하고 값으로 객체를 입력하면 된다
useState({
    enteredTitle:'',
    enteredAmount:'',
    enteredDate:''
  })
 
  • 상태를 업데이트할 때 하나가 아니라 세 개의 프로퍼티를 모두 업데이트해줘야 한다
 const [userInput, setUserInput] = useState({
    enteredTitle: "",
    enteredAmount: "",
    enteredDate: "",
  });
  const titleChangeHandler = (event) => {
    setUserInput({
      ...userInput, // 객체를 선택해 모든 키-값 쌍을 가져와 새 객체에 추가하는 것
      enteredTitle: event.target.value,  // 이거 말고 나머지 두 데이터도 놓치지 않도록!
    });
  };

  const amountChangeHandler = (event) => {
    setUserInput({
      ...userInput,
      enteredAmount: event.target.value,
    });
  };

  const dateChangeHandler = (event) => {
    setUserInput({
      ...userInput,
      enteredDate: event.target.value,
    });
  };
 

 

 

이전 상태에 의존하는 상태 업데이트

setUserInput({
      ...userInput,
      enteredTitle: event.target.value,
    });
 
  • 여기서 상태를 업데이트할 때 이전 상태에 의존하는 이유는 상태를 세 개 만드는 접근법이 아닌 하나의 상태만 활용하는 접근법을 사용했기 때문이다, 다른 값들을 잃지 않도록 복사해 줘야 했다
  • 따라서 이전 상태의 스냅샷에 기대서 기존의 값을 복사한 뒤 새 값으로 이전 값을 오버라이드 한 것
  • 상태를 업데이트할 때 이전 상태에 의존할 경우 또는 1씩 증가하는 카운터를 관리하는 경우에도 상태를 업데이트하고 이전 상태에 의존한다면 저런 식으로 가 아니라 상태 업데이트 함수의 대체 양식을 사용해야 한다
  • setUserInput 함수를 호출한 다음 이 안에 익명 화살표 함수처럼 함수를 넣어야 한다
  • setUserInput에 입력한 함수는 리액트가 자동으로 실행시킬 것이다
  • 그리고 업데이트 함수를 호출하는 그 상태에 대한 이전 상태 스냅샷을 받아야 한다
  • 이전 상태 스냅샷을 얻고 나면 그 아래에 새 상태 스냅샷을 리턴해야 한다
  • 전개 연산자(...prevState)로 이전 상태에서 키-값 쌍을 복사한 다음 enteredTitle을 event.target.value로 오버라이드 한다
  • 다수의 상태 업데이트를 동시에 예약할 경우 오래되었거나 잘못된 상태 스냅샷에 의존하게 될 수도 있다
const titleChangeHandler = (event) => {
    setUserInput((prevState) => {
      return { ...prevState, enteredTitle: event.target.value };
    });
  };
 
  • 이 내부 함수에서 제공하는 상태 스냅샷이 예약된 모든 상태 업데이트를 기억하고 항상 최신 상태 스냅샷이 되도록 보장해 준다
  • 이전 상태에 의존해 상태를 업데이트할 경우에는 꼭 이 함수 양식 사용하기

 

 

양식 입력에 상태 사용하기

  • 입력한 문자 메시지가 유효한 경우(길이가 3글자 이상) 입력 필드 아래에 Valid message라는 텍스트 표시
  • 유효하지 않으면 Invalid message라는 텍스트 표시
import React, { useState } from 'react';

import './styles.css';

export default function App() {
    const [messageValidity, setMessageValidity] = React.useState('Invalid');
   
    function messageChangeHandler(event){
        //할일:로직 추가하기
        const value = event.target.value; //입력된 값을 추출하고 조건에 따라 상태 업데이트
        if(value.trim().length<3){
            setMessageValidity('Invalid');
        }
        else{
            setMessageValidity('Valid');
        }
    }
  
    return (
        <form>
            <label>Your message</label>
            <input type="text" onChange={messageChangeHandler}/>
            <p>{messageValidity} message</p>
        </form>
    );
}
 

 

이전 상태 기반으로 상태 업데이트하기

  • "증가" 버튼 클릭할 때마다 증가하는 기본 카운터 만들기
import React from 'react';

import './styles.css';

export default function App() {
    const [counter, setCounter]= React.useState(0);
    
    function incrementCounterHandler(){
        setCounter(prevCounter => prevCounter + 1); 
    }
    
    return (
      <div>
        <p id="counter">{counter}</p>
        <button onClick={incrementCounterHandler}>Increment</button>
      </div>
    );
}
 
  • 이전 값을 기반으로 일부 상태를 업데이트할 때는 상태 업데이트 함수(setCounter)에 함수 전달해야 한다

 

 

대안: 공유 핸들러 함수 생성하기

  • 여러 개의 핸들러 함수를 하나로
  • event 대신 인자 이름과 값을 받는다
  • 하나의 함수로 인풋 요소 전부를 대상으로 발생하는 onChange 이벤트를 처리하려는 것
  • 인자 이름과 값으로 각각의 인풋 요소를 식별해야 한다
 const inputChangeHandler = (identifier, value)=> {
    if(identifier === 'title'){
      setEnteredTitle(value);
    } else if (identifier === 'date'){
      setEnteredDate(value);
    } else {
      setEnteredAmount(value);
    }
 
  • onChange에 inputChangeHandler로 그냥 넘기며 안된다
  • 새 익명 화살표 함수를 호출해 줘야 하는 곳에 생성한다
  • 이 화살표 함수는 인풋에 변경이 발생할 때마다 리액트가 발생시켜준다
 
  • 인풋이 제목용 인풋이니까 첫 번째 인자로 식별자를 'title'을 넣어주어 if 문으로 들어갈 수 있게 한다
  • 두 번째 인수는 변경된 값을 넣어준다 -> event.target.value
import React, { useState } from "react";
import "./ExpenseForm.css";

const ExpenseForm = () => {
  const [enteredTitle, setEnteredTitle] = useState("");
  const [enteredAmount, setEnteredAmount] = useState("");
  const [enteredDate, setEnteredDate] = useState("");

  const inputChangeHandler = (identifier, value)=> {
    if(identifier === 'title'){
      setEnteredTitle(value);
    } else if (identifier === 'date'){
      setEnteredDate(value);
    } else {
      setEnteredAmount(value);
    }

  }

  return (
    <form>
      <div className="new-expense__controls">
        <div className="new-expense__control">
          <label>Title</label>
          <input type="text" onChange={(event)=>inputChangeHandler('title', event.target.value)} />
        </div>
        <div className="new-expense__control">
          <label>Amount</label>
          <input
            type="number"
            min="0.01"
            step="0.01"
            onChange={(event)=>inputChangeHandler('amount', event.target.value)}
          />
        </div>
        <div className="new-expense__control">
          <label>Date</label>
          <input
            type="date"
            min="2019-01-01"
            max="2022-12-31"
            onChange={(event)=>inputChangeHandler('date', event.target.value)}
          />
        </div>
      </div>
      <div className="new-expense__actions">
        <button type="submit">Add Expense</button>
      </div>
    </form>
  );
};

export default ExpenseForm;
 

 

 

양식 제출 처리하기

  • 버튼 타입이 submit이고 form 요소 안에서 클릭되면 이 form 요소 자체에서 이벤트를 발생시키고 그 이벤트를 수신해야 한다
  • 그래서 form이 대상이 된다
 
  • 이건 브라우저의 기본 기능 - 폼에서 제출했을 때 개발서버한테 넘어감
const submitHandler = (event) => {
    event.preventDefault();  //preventDefault 메서드는 자바스크립트의 기본 메서드
  };
 
  • 요청이 자동으로 발송되는 기본 기능을 막아주기 때문에 페이지가 다시 로딩되지 않는다
  • 현재 로딩돼 있는 페이지가 그대로 있는 상태이고 요청을 어디에도 보내지 않고 상황을 처리할 수 있다
const submitHandler = (event) => {
    event.preventDefault();

    const expenseData = {  //객체 생성
      title: enteredTitle,
      amount: enteredAmount, //오른쪽은 상태변수 나타냄
      date: new Date(enteredDate), //날짜 문자열 파싱해서 날짜 객체로 변환
    };
    console.log(expenseData);
  };
 
 

 

 

양방향 바인딩 추가하기

  • 인풋 필드의 입력값 비워주기
  • 양방향 바인딩이란 인풋 요소에 발생하는 변경사항을 수신하는 것뿐 아니라 인풋 요소에 새 값을 넣을 수 있다는 뜻
  • 즉 코드를 이용해 인풋의 값을 초기화하거나 바꿀 수 있다는 것
  • value라는 속성을 추가해 주면 되는데 이것은 인풋 요소의 기본 속성이기도 하다
  • 내부 속성인 value에 값을 지정해 주는 것이다
 
  • 인풋에 발생한 변경 사항을 수신해 상태를 업데이트하는 것뿐 아니라 상태를 다시 인풋 요소에 전달해서 상태가 바뀌었을 때 인풋 요소도 바꿔준다
  • 이 방식의 이점은 양식이 제출됐을 때 이렇게 setEnteredTitle을 호출해서 빈 문자열로 인풋을 채울 수 있다
  • value를 추가하고 enteredTitle을 가리키게 한다
  • 양방향 바인딩은 양식 작업을 할 때 유용하다
  • 사용자의 입력을 받을 수도 있고 그 값을 우리가 바꿔 넣을 수도 있다

 

 

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

  • 우리가 폼으로 수집하고 생성한 데이터를 전달해야 한다. ExpenseForm에서 App 컴포넌트로
  • 속성은 부모와 자식 사이에서만 전달이 가능하고 중간 컴포넌트를 건너뛸 수 없다
  • 먼저 지출 데이터를 NewExpense로 전달해야 한다
  • ExpenseForm에 넣어주는 속성의 값은 함수이고 이 함수는 컴포넌트에서 뭔가 발생했을 때 호출된다
  • 사용자가 입력된 지출 데이터를 저장할 때 (양식이 제출됐을 때)

 

NewExpense.js

import React from "react";
import "./NewExpense.css";
import ExpenseForm from "./ExpenseForm";

const NewExpense = () => {
  const SaveExpenseData = (enteredExpenseData) => { //인자로는 입력된 지출 데이터
    const expenseData = {
      ...enteredExpenseData, //submitHandler에서 생성했던 객체
      id: Math.random().toString(),  //새 키 추가
    };
    console.log(expenseData);
  };

  return (
    <div className="new-expense">
      <ExpenseForm onSaveExpenseData={SaveExpenseData} />  //함수를 가리키는 포인터가 ExpenseForm에 전달되는 것
    </div>
  );
};

export default NewExpense;
 
 
 
submitHandler 안에 작성

 

  • 중요한 것은 ExpenseForm에서 실행한다는 것
  • 실행할 수 있는 이유는 여기서 받는 onSaveExpenseDate 키를 통해 받는 값이 함수(SaveExpenseData)이기 때문이다
  • onSaveExpenseData 속성을 통해 포인터를 전달했기 때문에 다른 컴포넌트에서 함수 실행 가능
 
  • submitHandler에서 생성한 데이터를 인수로 넘긴다
  • 이렇게 값을 전달해 주고 NewExpense에서 인자 형태로 그 값을 받는다
  • 함수 포인터가 saveExpenseDateHandler
  • 함수 준비하고 속성에 함수 포인터를 넘겨야 하는데 그 함수를 호출해서 데이터를 위로 전달하면 하향 함수에서 props 이용해서 호출할 수 있게 된다

 

ExpenseForm.js

  const submitHandler = (event) => {
    event.preventDefault();

    const expenseData = {
      title: enteredTitle,
      amount: enteredAmount,
      date: new Date(enteredDate),
    };

    props.onSaveExpenseData(expenseData);
    setEnteredTitle("");
    setEnteredAmount("");
    setEnteredDate("");
  };
 

NewExpense.js

const NewExpense = (props) => {
  const saveExpenseDataHandler = (enteredExpenseData) => {
    const expenseData = {
      ...enteredExpenseData,
      id: Math.random().toString(),
    };
    props.onAddExpense(expenseData);  //여기서 전달하는 건 지출 데이터
  };

  return (
    <div className="new-expense">
      <ExpenseForm onSaveExpenseData={saveExpenseDataHandler} />
    </div>
  );
};
 

App.js

  const addExpenseHandler = (expense) => {
    console.log("In App.js");
    console.log(expense);
  };

  return (
    <div>
      <NewExpense onAddExpense={addExpenseHandler} />
      <Expenses items={expenses} />
    </div>
  );
};

export default App;
 
 

 

 

상태 위로 올리기

  • 자식 컴포넌트에서 부모 컴포넌트로 데이터를 올리는 것
  • 그것을 부모 컴포넌트에서 활용하거나 그 부모의 다른 자식 컴포넌트에게 전달할 수 있다
  • 항상 최상위 컴포넌트로 전달하는 것이 아니라 필요한 곳까지 올려야 한다
 
  • NewExpense 컴포넌트는 데이터랑 상태를 생성하는 컴포넌트로 사용자가 입력한 데이터를 가져오는 작업을 한다
  • NewExpense 컴포넌트에 데이터를 저장하지 않고 대신 그 데이터를 App 컴포넌트로 보낸 후 App 컴포넌트에서 addExpenseHandler를 이용한다

 

 

파생/계산된 상태

  • 먼저 새로운 상태를 정의하고 함수를 만들어줄 수 있지만 상태가 중복된다
  • 대신 변수를 하나 추가하고 이 변수의 값을 동적으로 계산
  • 설정하는 상태가 결국 다른 상태와 직접적으로 연결된다면 별도의 상태로 관리하는 건 좋지 않다
const [filteredYear, setFilteredYear] = useState('2020');

let filterInfoText = '2019, 2021 & 2022';  //계산된 값. 상태에서 파생된 값

if(filteredYear === '2019') {
  filterInfoText = '2020, 2021 & 2022';
} else if (filteredYear === '2021') {
  filterInfoText = '2019, 2020 & 2022';
} else {
  filterInfoText = '2019, 2020 & 2021';
}
 
  • 값이 상태로 관리되었던 게 아닌데도 년도대로 필터링이 된다

 

 

제어 컴포넌트 대 비제어 컴포넌트, 상태 비저장 컴포넌트 대 상태 저장 컴포넌트

  • 값이랑 값에 발생하는 변경 작업은 컴포넌트 자체에서 처리하지 않고 부모 컴포넌트에서 처리한다
  • 리액트 앱에서 상태를 관리하는 컴포넌트가 몇 개 있다

 

 

퀴즈

React로 작업 시 이벤트를 수신하지 않는 방법은?

  • 수동으로 이벤트 리스너(ex) "addEventListner"를 통해)를 추가한다

 

onClick과 같은 이벤트 리스너 프로퍼티에는 어떤 값을 전달해야 하나요?

  • 이벤트가 발생했을 때 실행해야 하는 함수의 포인터
  • 실행될 함수의 '포인터'를 onClick 등의 값으로 전달하면 이벤트가 발생했을 때 '사용자를 대신하여' React가 함수를 실행한다

 

어떻게 하면 컴포넌트 중 하나에서 부모 컴포넌트, 즉 상위 수준으로 통신할 수 있나요?

  • 프로퍼티를 통해 함수를 받아들이고 이를 하위 수준(자식) 컴포넌트 내부로부터 호출하고 해당 함수를 부모 컴포넌트에 전달하는 방식으로 일부 작업을 트리거 할 수 있다

 

컴포넌트가 화면에 표시하는 내용은 어떻게 변경할 수 있나요?

  • useState를 통해 '상태'값을 생성해 JSX에서 변경 및 출력한다

 

변경 및 사용에 일반 JS 변수 대신 추가적인 '상태' 개념이 필요한 이유는 무엇인가요?

  • 표준 JS 변수는 React 컴포넌트를 재평가하지 않기 때문이다

 

useState에 관한 설명으로 옳지 않은 것은 무엇인가요?

  • useState를 다시 호출하면 상태 값이 업데이트된다

 

useState로 생성된 컴포넌트 상태는 어떻게 업데이트할 수 있나요?

  • useState도 반환하는 상태 업데이트 함수를 호출한다

 

하나의 단일 컴포넌트로 얼마나 많은 상태를 관리할 수 있나요?

  • 상태 slice를 필요하거나 원하는 만큼 가질 수 있다

 

const [counter, setCounter] = useState(1);
...
setCounter(counter + 1);
 

이 코드 조각에 무슨 문제가 있나요?

  • 이전 상태에 의존하는 상태를 업데이트한다면, 상태 업데이트 함수의 '함수 양식'을 대신 사용해야 한다

 

728x90