[TIL] Unit 6. React State & Props

string_main·2022년 6월 8일
0

React

목록 보기
3/7
post-thumbnail

인터넷 쇼핑몰에서 옷을 주문할 때, 원하는 옷을 장바구니에 담고 결제를 하려고 보니 다른 옷이 더 마음에 들어서 처음 담은 옷을 빼고 새로운 옷을 담으면 내가 결제해야 할 금액도 즉각적으로 변경된다. 이는 장바구니의 상태가 변한다고 말할 수 있다!

🌱 State & Props


React에서 데이터를 다루는 두 가지 방법에는 State와 Props가 있다.

  • State :

    • 살면서 변할 수 있는 값, 상태. (ex. 나이, 현재 사는 곳, 취업 여부 등)
    • 컴포넌트의 사용 중 컴포넌트 내부에서 변할 수 있는 값. (ex. Toggle Switch, Counter 등)
  • Props :

    • 컴포넌트의 속성(property)을 의미.
    • 외부, 상위 컴포넌트로부터 전달받은 변하지 않는 값 (ex. 이름, 성별 등)
    • props를 함수의 전달인자(arguments)처럼 전달받아 React 엘리먼트를 반환한다.
    • 함부로 변경될 수 없는(immutable) 읽기 전용(read-only) 객체
    • 클래스 컴포넌트 뿐 아니라 함수형 컴포넌트에서도 사용 가능. (Hook이 나오면서 함수 컴포넌트도 state를 다룰 수 있게 되었다.)

🌿 Props

props 사용 방법
1. 하위 컴포넌트에 전달하고자 하는 값(data)과 속성을 정의한다.
2. props를 이용하여 정의된 값과 속성을 전달한다. (속성명은 임의로 지정 가능, 여러 개 지정도 가능)
3. 전달받은 props를 렌더링한다.

  • 방법 1
// 컴포넌트 선언
function Parent() {
  return (
    <div className="parent">
      <h1>I'm the parent</h1>
      {/* JSX 속성 및 값 할당 방법 : <Child attribute={value} >*/}
	  <Child text={"I'm the eldest child"} />
    </div>
  );
};

// props 전달 및 렌더링
function Child(props) {
  return (
    <div className="child">
      <p>{props.text}</p>
    </div>
  );
};
  • 방법 2
function Parent() {
  return (
    <div className="parent">
        <h1>I'm the parent</h1>
        <Child>I'm the eldest child</Child>
    </div>
  );
};

function Child(props) {
  return (
    <div className="child">
        <p>{props.children}</p>
    </div>
  );
};
  • props 예제 :
import "./styles.css";

const App = () => {
  const itemOne = "React를";
  const itemTwo = "배우고 있습니다.";

  return (
    <div className="App">
      {/* Learn 컴포넌트에 itemOne 과 itemTwo 를
      props 로 전달하세요 */}
      <Learn one={itemOne} two={itemTwo}/>
    </div>
  );
};

const Learn = (props) => {
  // 전달받은 props 를 아래 <div> tag 사이에 사용하여
  // "React를 배우고 있습니다" 라는 문장이 렌더링되도록 컴포넌트를 완성하세요
  return <div className="Learn">{props.one} {props.two}</div>;
};

export default App;

🌿 State

  • Hook : 함수형 컴포넌트에서 사용되는 state와 관련된 기술들을 모아서 일컫는 말. 대표적으로 useState()useEffect()가 있다.

    • 리액트 컴포넌트는 state가 변경되면 새롭게 호출되고 리렌더링 된다.
    • state 값을 직접적으로 바꾸면 리렌더링이 의도대로 동작하지 않기 때문에 상태 변경 함수 호출로 변경해야 한다. (다른 메모리 주소를 가지는 깊은 복사로 갈아끼워주어야 한다.)
    • 만일, 하나의 상태를 기반으로 두 컴포넌트가 영향을 받는다면 이때에는 공통 부모 컴포넌트를 찾아 그곳에 상태를 위치해야 한다.
  • state 구별법 :

    • 부모로부터 props를 통해 전달된다면 state가 아니다.
    • 시간이 지나도 변하지 않으면 state가 아니다.
    • 컴포넌트 안의 다른 state나 props를 가지고 계산 가능하다면 state가 아니다.
  • useState

// useState 불러오기
import { useState } from "react";

function CheckboxExample() {
  // const [state 저장 변수, state 갱신 함수] = useState(상태 초기 값);
  const [isChecked, setIsChecked] = useState(false); // useState 호출 후 리턴 값을 구조 분해 할당한 변수

  const handleChecked = (event) => {
    setIsChecked(event.target.checked);
  };
  return (
    <div className="App">
      <input type="checkbox" checked={isChecked} onChange={handleChecked} />
      <span>{isChecked ? "Checked!!" : "Unchecked"}</span>
    </div>
  );
}
  • state 예제 :
// checkbox Ex
import React, { useState } from "react";
import "./styles.css";

function CheckboxExample() {
  const [isChecked, setIsChecked] = useState(false);
  /* 구조 분해 할당 코드를 풀어 쓴 것
  const stateHookArray = useState(false);
  const isChecked = stateHookArray[0];
  const setIsChecked = stateHookArray[1];
  */

  const handleChecked = (event) => {
    setIsChecked(event.target.checked);
  };
  return (
    <div className="App">
      <input type="checkbox" checked={isChecked} onChange={handleChecked} />
      <span>{isChecked ? "Checked!!" : "Unchecked"}</span>
    </div>
  );
}

export default CheckboxExample;

🌱 React 이벤트 처리 방식


DOM의 처리 방식과 유사하나 문법적으로 차이가 있다.

  • React에서 이벤트는 소문자 대신 카멜 케이스(camelCase)를 사용한다.
  • JSX를 사용해 문자열이 아닌 함수로 이벤트 처리 함수(Event handler)를 전달한다.
// html 이벤트 처리 방식
<button onclick="handleEvent()">Event</button>

// React 이벤트 처리 방식
<button onClick={handleEvent}>Event</button>
  • onChange : <input>, <textarea>, <select> 와 같은 폼(Form) 엘리먼트의 입력값이 변경될 때 마다 발생하는 이벤트이다. React에서는 이러한 변경될 수 있는 입력값을 일반적으로 컴포넌트의 state로 관리하고 업데이트한다.
function NameForm() {
  const [name, setName] = useState("");

  const handleChange = (e) => {
    setName(e.target.value); // 이벤트 객체에 담겨있는 input값을 읽어와서 setState를 통해 새로운 state로 갱신
  }

  return (
    <div>
      <input type="text" value={name} onChange={handleChange}></input>
      <h1>{name}</h1>
    </div>
  )
};
  • onClick : 사용자가 클릭을 했을 때 발생하는 이벤트로, 버튼이나 <a>를 통한 링크 이동 등과 같이 주로 사용자의 행동에 따라 애플리케이션이 반응해야 할 때 자주 사용하는 이벤트이다.
function NameForm() {
  const [name, setName] = useState("");

  const handleChange = (e) => {
    setName(e.target.value);
  }

  return (
    <div>
      <input type="text" value={name} onChange={handleChange}></input>
      <button onClick={alert(name)}>Button</button>
      <h1>{name}</h1>
    </div>
  );
};

위와 같이 onClick 이벤트에 alert(name) 함수를 바로 호출하면 컴포넌트가 렌더링 될 때 함수 자체가 아닌 함수 호출 결과가 onClick에 적용된다.

버튼을 클릭할 때가 아니라 컴포넌트가 렌더링 될 때에 alert가 실행되고 그 결과인 undefined가 onClick 에 적용되어 클릭했을 때 아무런 결과도 일어나지 않게 된다.

따라서 이벤트에 함수를 전달할 때는 함수를 호출하는 것이 아닌 아래와 같이 1) 리턴문 안에서 함수를 정의하거나 2) 리턴문 외부에서 함수 정의 후 이벤트에 함수 자체를 전달해야 하며, arrow function 을 사용하여 함수를 정의하여야 해당 컴포넌트가 가진 state에 함수들이 접근할 수 있다.

// 1) 함수 정의하기
return (
  <div>
	...
    <button onClick={() => alert(name)}>Button</button>
	...
  </div>
  );
};

// 2) 함수 자체를 전달하기
const handleClick = () => {
  alert(name);
};

return (
  <div>
      ...
    <button onClick={handleClick}>Button</button>
      ...
  </div>
  );
};
  • 예제 1) 이벤트 핸들러 연습
import "./styles.css";
import React, { useState } from "react";

function NameForm() {
  const [name, setName] = useState("");

  const handleChange = (e) => {
    setName(e.target.value);
  };

  const handleClick = () => {
    alert(name);
  };
  return (
    <div className="App">
      <h1>Event handler practice</h1>
      <input type="text" value={name} onChange={handleChange}></input>
      <button onClick={handleClick}>Button</button>
      {/* <button onClick={() => alert(name)}>Button</button> */}
      <h3>{name}</h3>
    </div>
  );
}
export default NameForm;
  • 예제 2) 드롭다운(drop down) 목록 상태 관리
import React, { useState } from "react";
import "./styles.css";

function SelectExample() {
  const [choice, setChoice] = useState("apple");

  const fruits = ["apple", "orange", "pineapple", "strawberry", "grape"];
  const options = fruits.map((fruit) => {
    return <option value={fruit}>{fruit}</option>;
  });
  console.log(choice);
  const handleFruit = (event) => {
    setChoice(event.target.value);
    //TODO : select tag 가 정상적으로 작동하도록 코드를 완성하세요.
  };

  return (
    <div className="App">
      <select onChange={handleFruit}>{options}</select>
      <h3>You choose "{choice}"</h3>
    </div>
  );
}

export default SelectExample;
  • 예제 3) 팝업(Pop up) 상태 관리
import React, { useState } from "react";
import "./styles.css";

function App() {
  const [showPopup, setShowPopup] = useState(false);

  const togglePopup = (event) => {
    if(event.target.className === "open") setShowPopup(true);
    else setShowPopup(false);
    // Pop up 의 open/close 상태에 따라 state 변경
  };

  return (
    <div className="App">
      <h1>Fix me to open Pop Up</h1>
      {/* 버튼을 클릭했을 때 Pop up 의 open/close 가 작동하도록
          button tag를 완성하세요. */}
      <button className="open" onClick={togglePopup}>
        Open me
      </button>
      {showPopup ? (
        <div className="popup">
          <div className="popup_inner">
            <h2>Success!</h2>
            <button className="close" onClick={togglePopup}>
              Close me
            </button>
          </div>
        </div>
      ) : null}
    </div>
  );
}

export default App;
  • 예제 4) input, textarea 상태 관리
import "./styles.css";
import React, { useState } from "react";

export default function App() {
  const [username, setUsername] = useState("");
  const [msg, setMsg] = useState("");

  return (
    <div className="App">
      <div>{username}</div>
      <input
        type="text"
        value={username}
        onChange={(event) => setUsername(event.target.value)}
        placeholder="여기는 인풋입니다."
        className="tweetForm__input--username"
      ></input>
      <div>{msg}</div>
      {/* TODO : 위 input과 같이 입력에 따라서 msg state가 변할 수 있게 
      아래 textarea를 변경하세요. */}
      <textarea
        placeholder="여기는 텍스트 영역입니다."
        className="tweetForm__input--message"
        onChange={(event) => setMsg(event.target.value)}
        value={msg}
      ></textarea>
    </div>
  );
}

🌱 React 데이터 흐름


React의 개발 방식의 가장 큰 특징은 페이지 단위가 아닌, 컴포넌트 단위로 시작한다는 점!

  • 페이지를 만들기 전에 컴포넌트를 먼저 만들고 조립한다. 즉, 상향식(bottom-up) 개발이다.
  • 테스트가 쉽고 확장성이 좋다.
  • 데이터는 위에서 아래로 흐른다.
  • 컴포넌트는 바깥에서 props를 이용해 데이터를 마치 인자(arguments) 혹은 속성(attributes)처럼 전달받을 수 있다.
  • 데이터를 전달하는 주체는 부모 컴포넌트이다. 즉, 데이터 흐름은 하향식(top-down) 이다.
  • React 단방향 데이터 흐름(one-way data flow)을 따른다!
  • State는 최소화하는 것이 가장 좋다.

🌱 Check List


  • state, props의 개념에 대해서 이해하고, 실제 프로젝트에 바르게 적용할 수 있다.

  • React 함수 컴포넌트(React Function Component)에서 state hook을 이용하여 state를 정의 및 변경할 수 있다.

  • React 컴포넌트(React Component)에 props를 전달할 수 있다.

  • 이벤트 핸들러 함수를 만들고 React에서 이용할 수 있다.

  • 실제 웹 애플리케이션의 컴포넌트를 보고 어떤 데이터가 state이고 props에 적합한지 판단할 수 있다. (중요!!!)

  • 실제 웹 애플리케이션 개발 시 적합한 state와 props의 위치를 스스로 정할 수 있다.

  • React의 단방향 데이터 흐름(One-way data flow)에 대해 자신의 언어로 설명할 수 있다.

  • JSX 문법의 기본과 컴포넌트 기반 개발에 대해서 숙지한다.

  • React Router DOM으로 React에서 SPA(Single-Page Application)을 구현할 수 있다.

  • state hook을 이용하여, 컴포넌트에서 데이터를 변화시킬 수 있다.

  • props를 이용하여, 부모 컴포넌트의 데이터를 자식 컴포넌트로 전달할 수 있다.

  • 바람직한 컴포넌트 구조와 state와 props의 위치에 대해 고민한다.

🌱 추가 학습


  • React에서의 데이터 흐름
  • click-to-edit ui 적용
  • local state 적용
  • React의 다양한 Hook들 학습
  • props.children
profile
FE developer

0개의 댓글