[React] React State & Props

jungmin Lee·2023년 9월 10일
0

데이터를 다루는 두 가지 방법, State와 Props

Props

Props는 컴포넌트의 속성(property)을 의미하며 변하지 않는 외부로부터 전달받은 값으로 함부로 변경될 수 없는 읽기 전용(read-only) 객체이다. 컴포넌트는 props를 입력받은 후 화면에 어떻게 표시되는지 기술하는 React 엘리먼트를 반환한다
props는 immutable한 데이터이며 부모 컴포넌트(상위 컴포넌트)로부터 전달받은 값으로 어떤 타입의 값도 넣어서 전달할 수 있도록 객체의 형태를 가진다. 부모에서 자식 또는 구성 요소 자체에서 데이터를 전달하는데 사용된다. props가 읽기 전용 객체가 아니라면 props를 전달받은 하위 컴포넌트 내에서 직접 수정 할 경우에 상위 컴포넌트의 값에 영향을 미치게 되며 side effect가 생기게 된다. 이 경우에는 React의 단방향, 하향식 데이터 흐름 원칙에 위배하게 된다.
입력값을 수정하지 않는 함수를 순수 함수라고 하는데, React 컴포넌트는 자신의 props를 다룰 때 순수 함수처럼 동작되어야 한다.

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

function Parent() {
  return (
    <div className="parent">
      <h1>Hello</h1>
      <Child text={"I'm the child"} />
    </div>
  );
};

function Child(props) {
  return (
    <div className="child">
      <p>{props.text}</p>
    </div>
  );
};

prop를 사용하기 위해 <Parent><Child> 컴포넌트를 선언하고 <Parent>컴포넌트 안에 <Child> 컴포넌트를 작성한다. text라는 속성을 선언하고 전달하는 값을 중괄호 { }를 이용하여 문자열 값을 할당한다.
함수에 인자를 전달하듯이 props를 전달하고 props는 객체이므로 { attribute : value }의 형태를 가지게 되며 dot notation을 사용하여 접근할 수 있다.

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

  return (
    <div className="App">
      <Learn>{`${itemOne} ${itemTwo}`}</Learn>
    </div>
  );
};

const Learn = (props) => {
  return <div className="Learn">{props.children}</div>;
};

export default App;

props를 전달하는 다른 방법으로 여는 태그와 닫는태그 사이에 값을 입력해주고 props.children을 이용해 value에 접근할 수 있다.


state

컴포넌트 내에서 변할 수 있는 값이며 상태는 React state로 다뤄야 한다. 예시로는 사용자의 입력한 검색어, 체크박스의 값이 있다.

State hook, useState
React에서는 state 를 다루는 방법 중 하나로 useState 라는 특별한 함수를 제공하며, useState를 이용하기 위해서는 React로부터 import 키워드로 useState를 불러와야 한다.
useState를 컴포넌트 안에서 호출하며, useState를 호출하는 것은 state라는 변수를 선언하는 것과 같다. 일반적인 변수는 함수가 끝날 때 사라지지만, state 변수는 React에 의해 함수가 끝나도 사라지지 않는다.

function CheckboxExample() {
  const [isChecked, setIsChecked] = useState(false);

  const handleChecked = (event) => {
    setIsChecked(event.target.checked);
  };

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

input[type=checkbox] JSX 엘리먼트의 값 변경에 따라서 isChecked 가 변경되어야 한다. input[type=checkbox] 엘리먼트의 값이 변경되면 onChange 이벤트가 발생하고 이벤트 핸들러 함수인 handleChecked 를 호출하고, 이 함수가 setIsChecked 를 호출하게 된다.. setIsChecked가 호출되면 호출된 결과에 따라 isChecked 변수가 갱신되며, React는 새로운 isChecked 변수를 CheckboxExample 컴포넌트에 넘겨 해당 컴포넌트를 다시 렌더링 한다.

useState 사용법
useState를 호출하면 배열을 반환한다. 배열의 0번째 요소는 현재 state 변수를 뜻하며, 1번째 요소는 이 변수를 갱신할 수 있는 함수이다. useState의 인자로 넘겨주는 값은 state의 초기값이다. state 저장 변수, state 갱신 함수는 useState의 리턴값을 구조 분해 할당한 변수이다. state 변수에 저장된 값을 사용하려면 JSX 엘리먼트 안에 직접 불러서 사용하면 된다.

const [state 저장 변수, state 갱신 함수]  = useState(상태 초기 값);

state 갱신하기

const [state, setState]  = useState(상태 초기 값);

state를 갱신하려면 state 변수를 갱신할 수 있는 함수인 setState를 호출한다. 함수의 이름은 자유롭게 작성해도 되지만, 변수를 갱신할 수 있는 함수에 set을 붙여서 많이 사용한다.

state hook 사용 시 주의점

  • React 컴포넌트는 state가 변경되면 새롭게 호출되고, 리렌더링 된다.
  • React state는 상태 변경 함수 호출로 변경해야 한다. 강제로 변경을 시도하면 리렌더링이 되지 않는다거나, state가 제대로 변경되지 않는다.

✨ Hook이 나오기 전에는 state는 클래스 컴포넌트에만 다룰 수 있었다. React 16.8 버전에 Hook이 나오면서 함수 컴포넌트도 state를 다룰 수 있다.


이벤트 처리 (이벤트 핸들링; Event handling)

React의 이벤트 처리 방식은 DOM의 이벤트 처리 방식과 유사하며, 소문자 대신 카멜 케이스를 사용한다. JSX를 사용하여 문자열이 아닌 함수로 이벤트 처리 함수를 전달한다.

  • HTML에서 이벤트 처리 방식
  <button onclick="handleEvent()">Event</button>
  • React의 이벤트 처리 방식
  <button onClick={handleEvent}>Event</button>

onChange
<input> <textarea> <select>와 같은 폼엘리먼트는 사용자의 입력값을 제어하는 데 사용된다. React에서는 변경될 수 있는 입력값을 일반적으로 컴포넌트의 state로 관리하고 업데이트한다. onChange 이벤트가 발생하면 e.target.value를 통해 이벤트 객체에 담겨있는 input 값을 읽어올 수 있다. input으로 에시를 들었을 때, onChange는 input의 텍스트가 바뀔 때마다 발생하는 이벤트이다. 컴포넌트 return 문 안의 input 태그에 value와 onChange를 넣어준다. 이벤트가 발생하게 되면 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 이벤트에 alert(name) 함수를 바로 호출하면 컴포넌트가 렌더링 될 때 함수 자체가 아닌 함수 호출의 결과가 onClick 에 적용된다. 그렇게 되면 버튼을 클릭할 때가 아닌, 컴포넌트가 렌더링 될 때에 alert가 실행되고 결과로 함수는 리턴 값이 없을 때 undefined 를 반환된다. 따라서 onClick 이벤트에 함수를 전달할 때는 함수를 호출하는 것이 아니라 리턴문 안에서 함수를 정의하거나 리턴문 외부에서 함수를 정의 후 이벤트에 함수 자체를 전달해야 한다.

  • 함수 정의하기
return (
  <div>
	...
    <button onClick={() => alert(name)}>Button</button>
	...
  </div>
  );
};
  • 함수 자체를 전달하기
const handleClick = () => {
  alert(name);
};

return (
  <div>
      ...
    <button onClick={handleClick}>Button</button>
      ...
  </div>
  );
  • Pop up의 open과 close를 state를 통해 관리하기
function App() {
  const [showPopup, setShowPopup] = useState(false);

  const togglePopup = () => {
    setShowPopup(!showPopup)
  };

return (
  <div className="App">
    <h1>Fix me to open Pop Up</h1>
    <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;

React 데이터 흐름

React의 개발 방식의 가장 큰 특징은 컴포넌트 단위로 시작한다는 점과 상향식(bottom-up)으로 앱을 만든다. 이것의 장점은 테스트가 쉽고 확장성이 좋다. 단일 책임 원칙에 따른 구분이며, 하나의 컴포넌트는 한 가지 일만 수행하도록 유도한다.
데이터는 위에서 아래로 흐르며, 컴포넌트는 컴포넌트 바깥에서 props를 이용해 데이터를 마치 인자나 속성처럼 전달받을 수 있다. 데이터를 전달하는 주체는 부모 컴포넌트가 되며, 데이터 흐름이 하향식(top-down)임을 의미한다. 단방향 데이터 흐름(one-way data flow)은 React를 대표하는 설명 중 하나일 정도로 중요하다.

상태가 많아질수록 애플리케이션은 복잡해진다. 상태는 최소화하는 것이 가장 좋으며, 모든 데이터를 상태로 둘 필요는 없습니다. 부모로부터 props를 통해 전달되거나, 시간이 지나도 변하지 않고 컴포넌트 안의 다른 state나 props를 가지고 계산 가능하다면 상태가 아니다.

하나의 상태를 기반으로 두 컴포넌트가 영향을 받는다면 공통 소유 컴포넌트를 찾아 그곳에 상태를 위치해야 한다. 데이터를 다룰 때는 컴포넌트들 간의 상호 관계와 데이터의 역할, 데이터의 흐름을 고려하여 위치를 설정해야 한다.

profile
Leejungmin

0개의 댓글