본문 바로가기

개발인생다반사/TIL(Today i learned)

TIL 210813 - React State & Props

React State & Props

 

갈 길이 멀다 어서 어서 가자!

 

Achievement Goals

  • state, props의 개념에 대해서 이해하고, 실제 프로젝트에 바르게 적용할 수 있다.
  • React 함수 컴포넌트(React Function Component)에서 state hook을 이용하여 state를 정의 및 변경할 수 있다.
  • React 컴포넌트(React Component)에 props를 전달할 수 있다.
  • 이벤트 핸들러 함수를 만들고 React에서 이용할 수 있다.
  • 실제 웹 애플리케이션의 컴포넌트를 보고 어떤 데이터가 state이고 props에 적합한지 판단할 수 있다.
  • 실제 웹 애플리케이션 개발 시 적합한 state와 props의 위치를 스스로 정할 수 있다.
  • React의 단방향 데이터 흐름(One-way data flow)에 대해 자신의 언어로 설명할 수 있다.

 

시작!

Intro

state : 컴포넌트를 사용 중 컴포넌트 내부에서 변할 수 있는 값

prors : 외부로 부터 전달 받은 값

 

on/off 여부

{isOn: true}

{isOn: false}

 

counter

{count: 0}

{count: 3}

{count: 6}

 

 

Props 특징

  • 컴포넌트의 속성을 의미
  • 부모 컴포넌트(상위 컴포넌트)로 부터 전달받은 값
  • props는 React의 컴포넌트에 함수의 인자 역할을 한다.

React는 컴포넌트는 props를 전달인자로 받아

이를 기반으로 화면에 어떻게 표시는 되는지를 기술하는

React 엘리먼트를 반환한다.

  • 객체 타입이다.
  • 읽기 전용이다.(read-only)

React의 데이터는 단방향 흐름을 가진다.

그러므로 읽기전용객체가 올바르다.

그러지 않을 경우 props를 전달받은

하위 컴포넌트 내에서 직접 수정될 경우

상위 컴포넌트의 값에 영향을 끼치게 된다.

 

props를 사용하는 방법은 아래와 같이 3단계 순서로 나눌 수 있다.

  1. 하위 컴포넌트에 전달하고자 하는 값(data)과 속성을 정의한다.
  2. props를 이용하여 정의된 값과 속성을 전달한다.
  3. 전달받은 props를 렌더링한다.

<Parent> 와 <Child> 라는 컴포넌트를 선언하고,

<Parent> 컴포넌트 안에 <Child> 컴포넌트를 작성한다.

 

function Parent() {
  return (
    <div className="parent">
      <h1>I'm the parent</h1>
      <Child text={"I'm the eldest child"} />
      {/* Child 컴포넌트에 또 다른 문자열을 props 로 전달해 보세요 */}
      <Child />
    </div>
  );
}

function Child(props) {
  console.log("props : ", props);
  return (
    <div className="child">
      <p>{props.text}</p> //{props.children} 의 형태로도 전달할 수 있다.
    </div>
  );
}

export default Parent;

 

<Child attribute={value} />

[코드] React에서 JSX 속성 및 값을 할당하는 방법 1

State 

컴포넌트 내에서 변할 수 있는 값, 즉 상태는 React state로 다뤄야 한다.

장바구니에서 물건을 체크하는 여부

 

import React, { useState } from "react"; // useState를 사용하기 위해 import한다.
import "./styles.css";

function CheckboxExample() {
  const [isChecked, setIsChecked] = useState(false);// 새로운 state 변수를 선언하고, 여기서는 이것을 isChecked 라 부르겠습니다.

  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;

 

useState 사용법

React에서 state를 다루는 방법 중 useState라는 특별한 함수를 제공한다.

 

useState를 컴포넌트 안에서 호출해준다. 호출한다는 것은 state라는 변수를 선언하는

것과 동일하다. 다만 변수의 이름은 아무 이름으로 지어도 된다.

변수는 함수가 끝나면 사라지지만 state변수는 사라지지 않는다.

 

문법적으로 보면 위 예시의 isChecked, setIsChecked 는

useState 의 리턴값을 구조 분해 할당한 변수이다.

function CheckboxExample() {
  // 1번 코드를 풀어쓰면
  const [isChecked, setIsChecked] = useState(false); // 1번
  //const [state 저장 변수, state 갱신 함수] = useState(상태 초기 값);

  //...

  // 2번 코드와 같습니다.
  const stateHookArray = useState(false); // 2번
  const isChecked = stateHookArray[0];
  const setIsChecked = stateHookArray[1];
}
  • isChecked : state를 저장하는 변수
  • setIsChecked : state isChecked 를 변경하는 함수
  • useState : state hook
  • false : state 초기값
const [state 저장 변수, state 갱신 함수] = useState(상태 초기 값);

이 state변수 isChecked를 사용하려면 JSX엘리먼트 안에 직접 불러서 사용한다.

isChecked가 boolean값을 가지기 때문에

삼항연산자를 사용한다. 굿!!! 좋은 기능이네

 

state 갱신하기

더보기
  • state를 갱신하려면 state 변수를 갱신할 수 있는 함수인 setIsChecked 를 호출합니다.
  • 이번 예시의 경우, input[type=checkbox] JSX 엘리먼트의 값 변경에 따라서 isChecked 가 변경되어야 합니다. 브라우저에서 checked로 값이 변경되었다면, React의 isChecked 도 변경되어야겠죠?
  • input[type=checkbox] 엘리먼트의 값이 변경되면 onChange 이벤트가 발생하고, 이벤트 핸들러 함수가 작동되는 패턴은 DOM을 다뤄보시면서 익숙해지셨죠? 유효성 검사 스프린트에서 input[type=text] 엘리먼트의 값이 변경될 때, 이벤트 핸들러 함수를 작동시키는 패턴을 복습해보세요.
  • React도 마찬가지입니다. 사용자가 체크박스 값을 변경하면 onChange 이벤트가 이벤트 핸들러 함수인 handleChecked 를 호출하고, 이 함수가 setIsChecked 를 호출하게 됩니다. setIsChecked 가 호출되면 호출된 결과에 따라 isChecked 변수가 갱신되며, React는 새로운 isChecked 변수를 CheckboxExample 컴포넌트에 넘겨 해당 컴포넌트를 다시 렌더링 합니다.
  • React state는 상태 변경 함수 호출로 변경해야 합니다. 강제로 변경을 시도하면 안 됩니다. 상태 변경 함수 사용은 React와 개발자의 약속이기 때문에 지켜주셔야 합니다. 강제로 변경을 시도하면, 리렌더링이 되지 않는다거나, state가 제대로 변경되지 않습니다.

 

 

이벤트 처리

React에서의 이벤트 핸들리 방식은 DOM과 유사하나 차이가 있음

 

  • React 에서 이벤트는 소문자 대신 카멜 케이스(camelCase) 를 사용합니다.
  • JSX를 사용하여 문자열이 아닌 함수로 이벤트 처리 함수(이벤트 핸들러; Event handler)를 전달합니다.
<button onClick={handleEvent}>Event</button>

 

onChange

 

더보기

<input> <textarea> <select> 와 같은 폼(Form) 엘리먼트는 사용자의 입력값을 제어하는데 사용됩니다. React 에서는 이러한 변경될 수 있는 입력값을 일반적으로 컴포넌트의 state 로 관리하고 업데이트합니다. onChange 이벤트가 발생하면 e.target.value 를 통해 이벤트 객체에 담겨있는 input 값을 읽어올 수 있습니다. 컴포넌트 return 문 안의 input 태그에 valueonChange 를 넣어주었습니다. onChangeinput 의 텍스트가 바뀔 때 마다 발생하는 이벤트입니다. 이벤트가 발생하면 handleChange 함수가 작동하며, 이벤트 객체에 담긴 input 값을 setState 를 통해 새로운 state 로 갱신합니다.

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

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

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

 

onClick

더보기

onClick 이벤트는 말 그대로 사용자가 클릭이라는 행동을 하였을 때 발생하는 이벤트입니다. 버튼이나 <a> tag 를 통한 링크 이동 등과 같이 주로 사용자의 행동에 따라 애플리케이션이 반응해야 할 때 자주 사용하는 이벤트입니다. 그럼 위의 onChange 예시에 버튼을 추가하여 버튼 클릭 시 input tag 에 입력한 이름이 alert을 통해 알림창이 팝업되도록 코드를 추가해 보겠습니다.

잘못된 방법

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 (함수는 리턴값이 없을 때 undefined 를 반환합니다.) 가 onClick 에 적용되어 클릭했을 때 아무런 결과도 일어나지 않습니다. 따라서 onClick 이벤트에 함수를 전달할 때는 함수를 호출하는 것이 아니라 아래와 같이 리턴문 안에서 함수를 정의하거나 리턴문 외부에서 함수를 정의 후 이벤트에 함수 자체를 전달해야 합니다. 단, 두 가지 방법 모두 arrow function 을 사용하여 함수를 정의하여야 해당 컴포넌트가 가진 state에 함수들이 접근할 수 있습니다.

올바른 방법

// 함수 정의하기

return (
  <div>
	...
    <button onClick={() => alert(name)}>Button</button>
	...
  </div>
  );
};

// 함수 자체를 전달하기

const handleClick = () => {
  alert(name);
};

return (
  <div>
      ...
    <button onClick={handleClick}>Button</button>
      ...
  </div>
  );
};
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;





unction SelectExample() {

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



const fruits = ["apple", "orange", "pineapple", "strawberry", "grape"];

const options = fruits.map((fruit) => {

return <option value={fruit}>{fruit}</option>;

});



const handleFruit = (event) => {

setChoice(event.target.value)

};



return (

<div className="App">

<select type ="drop" value={choice} onChange={handleFruit}>

{options}

</select>

<h3>You choose "{choice}"</h3>

</div>

);

}



export default SelectExample;

 

Controlled Component 

React가 state를 통제할 수 있는 컴포넌트

어떻게 React가 state를 통제할 수 있을까?

input에 값 입력 시, state도 그때그때 바뀌면(onChange) 된다..

그리고 이 변경된 state와 input의 value 또한 같게 작성해야 한다.

 

주의사항 textarea는 단순히 value를 지정해주는게 아니라

속성의 value를 쓰기 때문에 this.msg로 처리

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>
      <textarea
        value={this.msg}
        onChange={(event) =>  setMsg(event.target.value)}
        placeholder="여기는 텍스트 영역입니다."
        className="tweetForm__input--message"
      ></textarea>
    </div>
  );
}

 

React 데이터 흐름

React에서는 컴포넌트 단위로 페이지를 쪼개서 생각해야 한다.

프로토타입에서 컴포넌트 찾아내고 페이지를 조립해 간다.

 

그 후 컴포넌트를 좀 더 세부적으로 디자인 하였다.

컴포넌트 바깥에서 props를 이용해 데이터를 인자나 속성으로 전달받는다.

 

앱을 만들때 필요한 데이터가 무엇인지 정의해야 한다.

state는 되도록이면 적게 만드는 방향.

state가 많으면 앱은 복잡해진다.

 

부모로부터 props를 통해 전달됩니까? 그러면 확실히 state가 아닙니다.

- 시간이 지나도 변하지 않나요? 그러면 확실히 state가 아닙니다.

- 컴포넌트 안의 다른 state나 props를 가지고 계산 가능한가요? 그렇다면 state가 아닙니다.

 

상태 위치 정하기도 중요하다.

상태가 특정 컴포넌트에만 적용되면 상관없으나

만일 하나의 상태를 기반으로 다수의 컴포넌트가 영향을 받는다면 공통컴포넌트를 찾아

그곳에 상태를 위치해야 한다.

 

setShowPopup

 

 

React Twittler State & Props

Achievement Goals

  • JSX 문법의 기본과 컴포넌트 기반 개발에 대해서 숙지한다.
  • React Router DOM으로 React에서 SPA(Single-Page Application)을 구현할 수 있다.
  • state hook을 이용하여, 컴포넌트에서 데이터를 변화시킬 수 있다.
  • props를 이용하여, 부모 컴포넌트의 데이터를 자식 컴포넌트로 전달할 수 있다.
  • 바람직한 컴포넌트 구조와 state와 props의 위치에 대해 고민한다.