useState → setState 에 대해서

이동명·2023년 6월 23일
0
post-thumbnail

1.**setState 는 새로운 값을 가지고 컴포넌트를 다시 불러줍니다.**

function Counter() {
  const [count, setCount] = useState(0);

  function handleAlertClick() {
    setTimeout(() => {
      alert('You clicked on: ' + count);
    }, 3000);
  }
  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
      <button onClick={handleAlertClick}>
        Show alert
      </button>
    </div>
  );
}

위의 코드는 간단하게 버튼이 두 개 있습니다. 하나의 버튼은 count 라는 상태값을 증가시켜주며, 또 다른 버튼은 count라는 상태값을 3초 뒤에 alert 해줍니다.

여기서 다음과 같이 행동을 해봅니다.

💡 카운터를 3으로 증가시킵니다. ► "Show alert"버튼을 누릅니다. ► 타임아웃(3초)가 지나기 전에 카운터를 5로 증가시킵니다.

결과는 바로 3이 나옵니다. 왜냐하면 state는 리렌더링이 될 때 아예 새로운 함수를 반환해주기 때문입니다.

// 처음 랜더링 시
function Counter() {
  const count = 0; // useState() 로부터 리턴
  // ...
  function handleAlertClick() {
    setTimeout(() => {
      alert('You clicked on: ' + count);
    }, 3000);
  }
  // ...
}

// 클릭하면 함수가 다시 호출된다
function Counter() {
  const count = 1; // useState() 로부터 리턴
  // ...
  function handleAlertClick() {
    setTimeout(() => {
      alert('You clicked on: ' + count);
    }, 3000);
  }
  // ...
}

// 또 한번 클릭하면, 다시 함수가 호출된다
function Counter() {
  const count = 2; // useState() 로부터 리턴
  // ...
  function handleAlertClick() {
    setTimeout(() => {
      alert('You clicked on: ' + count);
    }, 3000);
  }
  // ...
}

이런 식으로 함수가 재생성이 되는 것..각각의 함수에서 count는 독립적인 것.. 따라서 count가 3일때 버튼을 눌렀다면, alert를 해주는 함수는 이 때의 상태값은 count == 3 을 기억하고 있을 것이며 그 것을 alert 해준다.. 이 것은 실제의 함수에서도 발생하는 상황입니다.

2.**상태는 리렌더링의 조건이자 함수의 상수**

위 사진이 바로 컴포넌트가 없데이트 되어서 화면에 보이기까지의 순서를 담은 것 이다. 함수에서 proprs가 바뀌거나 state가 바뀌면 shouldComponentUpdate 과정이 진행됩니다.

3.**prevState를 잘 사용해보자!**

setState함수는 비동기 함수이다. 1번에서 새로운 컴포넌트를 불러오는 과정에서 원래의 함수가 계속 진행이 되었던 것도 결국에는 비동기함수이기 때문에 가능했던 것입니다. 자 그럼 비동기 함수이기 때문에 발생하는 문제점이 있다.

import React, { useState } from "react";

function App() {
  const initialNum = 0;
  const [num, setNum] = useState(initialNum);

  const IncrementByFive = () => {
    for (let i = 0; i < 5; i++) {
      setNum(num + 1);
      console.log(num);
    }
  };

  return (
    <div>
      <p>Counter: {num}</p>
      <button onClick={() => setNum(initialNum)}>Reset</button>
      <button onClick={() => setNum(num + 1)}>Increment</button>
      <button onClick={() => setNum(num - 1)}>Decrement</button>
      <button onClick={IncrementByFive}>Increment By 5</button>
    </div>
  );
}
export default App;

IncrementByFive 이 실행되면 그냥 1씩 증가한다. 이는 setState가 비동기함수이기 때문 입니다. 이 때 컴포넌트에서 어떤 상황이 일어나는지 보십시오.

https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https://blog.kakaocdn.net/dn/bSsxk6/btrwbrG4PLQ/nJJR98uP1UQ8WDsiFBNM5k/img.png

기존 함수는 원래의 진행방향으로 가고 있습니다. 첫 번째로 보이는 페이지에서 num을 변화시켜 리렌더링 시켰으니, 두 번째 페이지가 나오겠죠? 그런데 위에서 적어준 코드는 setState(1 + 1)을 다섯번 해준거나 마찬가지입니다. 이는 첫 번째 보이는 페이지의 num이 1이라고 한다면, 이 때 반복문을 다섯번 돌아주기 때문입니다.

setState(num + 1)이 다섯번 돌아간다는 것은, setState(1 + 1) 이 다섯번 돌아감과 동시에(!!!) 업데이트된 페이지를 렌더링 해줍니다. (비동기함수이기 때문에 반복문과 업데이트가 동시에 진행됩니다!) 따라서 setState(2) 가 다섯번 반복되니 처음 setState말고는 update를 할 필요가 없기 때문에 num이 2인 상태로 렌더링이 되고 마는것이죠.

prevState 사용해서 해결하기

import React, { useState } from "react";

function App() {
  const initialNum = 0;
  const [num, setNum] = useState(initialNum);

  const IncrementByFive = () => {
    for (let i = 0; i < 5; i++) {
      setNum((prevNum) => prevNum + 1);
    }
  };

  return (
    <div>
      <p>Counter: {num}</p>
      <button onClick={() => setNum(initialNum)}>Reset</button>
      <button onClick={() => setNum(num + 1)}>Increment</button>
      <button onClick={() => setNum(num - 1)}>Decrement</button>
      <button onClick={IncrementByFive}>Increment By 5</button>
    </div>
  );
}
export default App;

이 것은 setState의 콜백함수 인자에서 전에 사용하던 state을 가져왔기 때문에 가능한 것입니다.

!https://blog.kakaocdn.net/dn/peE1W/btrwhcVJOH4/OTMFJXp9iVg2M7t5i3qL51/img.png

이런 식으로 원래 컴포넌트에서 setState 가 다섯번 돌아간다고 해도, 기특하게 이전의 setState을 한 state의 값을 기억해서 계속계속 받아와줍니다. 그래서 결국 state가 잘 추가가 되는 것이지요.

profile
Web Developer

0개의 댓글