Props&State(feat:setState가 비동기인 이유)

황준승·2022년 1월 14일
0
post-thumbnail

목차

  1. 컴포넌트
  2. props
  3. state

1. 컴포넌트

리액트의 가장 큰 장점을 화면의 각 구성 요소를 컴포넌트로 나누어 이를 재사용하기 쉽게 만들었고 이를 통해 유지보수 또한 굉장히 쉽게 할 수 있다는 장점이 있습니다.

추가적으로 리액트 컴포넌트는 내장된 라이프 사이클 api를 통해서 컴포넌트가 화면에 나타날 때마다 주어진 작업들을 처리할 수 있습니다.

react에서 컴포넌트를 선언할 수 있는 방법은 class형 컴포넌트 함수형 컴포넌트크게 두 가지가 있습니다.

class vs 함수형

함수형 컴포넌트는 class형에 비해 선언하기도 쉽고 메모리 자원도 클래스형 컴포넌트보다 덜 사용합니다.

그렇지만 함수형 컴포넌트는 class형에 비해 state와 라이프사이클 api 의 사용이 불가능하다는 점입니다. 그렇지만 최근 v16.8 업데이트 이후 Hooks라는 기능이 도입되면서 이를 해결되었습니다.

리액트 공식메뉴얼에서도 Hooks 사용을 권장하고 있지만 우리는 class 문법도 충분히 익혀두어야 합니다. (옛날 코드들도 해석하기 위해)

2. props

props는 Immutable Data 즉, 변하지 않는 데이터입니다.
상위(부모) 컴포넌트에서 하위(자식) 컴포넌트로 데이터를 넘겨줄 때 사용합니다.

defaultProps

props의 기본값을 설정하는 도구입니다.

예제)

MyComponent.defaultProps = {
  name: "기본 이름"
};

이렇게 되면 부모로 부터 props를 받지 않고도 props에 대한 기본값으로 화면이 렌더링 될 수 있습니다.

태그 사이의 내용을 보여주는 children

컴포넌트 태그 사이의 내용을 보여주는 props

예제)

// app.js

export default function App() {
  return (
    <div className="App">
      <MyComponent name="React">리액트</MyComponent>
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>
    </div>
  );
}
// MyComponent.js
import React, { useState } from "react";


const MyComponent = ({ name, children }) => {
  const [count, setCount] = useState(0);

  return (
    <>
      <div>{name}</div>
      <div>{children}</div>
    </>
  );
};

MyComponent.defaultProps = {
  name: "기본 이름"
};

export default MyComponent;

ProTypes를 통한 props 검증

props의 타입을 확인함으로써 재사용과 유지 보수적인 측면에서 효율을 높여줄 수 있다.
=> 타입스크립트가 더 좋은 대안이 될 것 같다.


import PropTypes from "prop-types";

MyComponent.propTypes = {
  name: PropTypes.string
};

더 자세한 정보는 https://github.com/facebook/prop-types

3. state

컴포넌트 내부에서 바뀔 수 있는 값을 의미한다.

예제)

const MyComponent = ({ name, children }) => {
  const [count, setCount] = useState(0);

  const plusEvent = () => {
    setCount(count + 1);
    setCount(count + 1);
  };
  return (
    <>
      <div>{name}</div>
      <div>{children}</div>
      <span>{count} </span>
      <button onClick={plusEvent}>+</button>
    </>
  );
};

위의 코드에서 한 가지 문제점이 있다.
plusEvent의 setCount를 통해 +2작업을 하고 싶은데 결과는 +1로 도출된다.

그 이유는 바로 setState 작업이 비동기적으로 실행되기 때문이다.

그렇다면 setState가 비동기인 이유 ??

만약 리액트가 동기적으로 작동했다면 state가 변함에 따라 계속해서 페이지를 리렌더링 해야할 것이다. 그렇게 될 경우 성능적으로 굉장히 문제가 많을 것이다.

따라서 리액트 개발자들은 state의 상태를 한번에 종합해서 화면에 렌더링하기 위해 비동기적인 작업으로 이를 구현한 것이다.

정확히는 16ms 단위로 batch(일괄) update를 진행하고, 그사이 변경된 상태값을 모아서(merge) 이전의 엘리먼트 트리와 변경된 state가 적용된 엘리먼트 트리를 비교하는 작업(reconciliation)을 거쳐 최종적으로 변경된 부분만 DOM에 적용시킨다.

그렇기 때문에 state도 결국 객체이기 때문에, 같은 키 값을 가진 경우라면 가장 마지막 실행값으로 덮어져버리게 된다.

즉, 우리의 코드에서도 바꾸려고 하는 count의 값이 같으므로 제일 마지막 구문만 실행되는 것을 알 수 있다.

해결책

setState의 인자로 함수를 넣어주면 된다.

setState가 비동기 적으로 작동하지만, 렌더링 전에는 모두 batch되는 것이 보장되고, 실행 순서대로 처리됨이 보장된다. 인자로 넘겨주는 함수는 큐에 저장되기 때문에 순서대로 실행될 수 있다.

const MyComponent = ({ name, children }) => {
  const [count, setCount] = useState(0);

  const plusEvent = () => {
    setCount((prev) => prev + 1);
    setCount((prev) => prev + 1);
  };
  return (
    <>
      <div>{name}</div>
      <div>{children}</div>
      <span>{count} </span>
      <button onClick={plusEvent}>+</button>
    </>
  );
};

결과 : +2가 되는 것을 볼 수 있다.

이렇게 넣어준 함수의 인자로 이전 state객체가 전달되고, 이는 가장 최신값이 보장된다.
=> 큐에 저장된 함수가 순서대로 실행되면서 반환하는 값이 다음 함수의 preState로 들어가는 방식이기 때문

출처

리액트를 다루는 기술
setState가 비동기로 동작하는 이유
https://velog.io/@zuzokim/React-setState%EA%B0%80-%EB%B9%84%EB%8F%99%EA%B8%B0%EC%9D%B8-%EC%9D%B4%EC%9C%A0#:~:text=%EB%B0%A9%EC%8B%9D%EC%9D%B4%EA%B8%B0%20%EB%95%8C%EB%AC%B8.-,%EA%B2%B0%EA%B5%AD,%EC%9D%B8%20%EA%B2%83%EC%9D%84%20%EC%95%8C%20%EC%88%98%20%EC%9E%88%EB%8B%A4.

https://velog.io/@cada/React%EC%9D%98-setState%EA%B0%80-%EC%9E%98%EB%AA%BB%EB%90%9C-%EA%B0%92%EC%9D%84-%EC%A3%BC%EB%8A%94-%EC%9D%B4%EC%9C%A0

https://dongmin-jang.medium.com/reactjs-setstate-%EC%99%9C-%EB%B9%84%EB%8F%99%EA%B8%B0%EB%A1%9C-%EC%B2%98%EB%A6%AC%EB%90%98%EB%8A%94%EA%B0%80-8197d707ca6a

profile
다른 사람들이 이해하기 쉽게 기록하고 공유하자!!

0개의 댓글