[ React ] useState 의 렌더링과 함수형 업데이트

Maria Kim·2022년 1월 7일
13
post-thumbnail

useState함수형 업데이트와의 렌더링 시점을 알기 위해 여러 코드를 작성해 봤고 이를 통해 알아낸 부분들을 정리한 글이다.

setState의 비동기적 특성 vs 함수형 업데이트

setState는 비동기로 동작한다

export default function App() {
  const [value, setValue] =  useState(0)

  const onClick = () => {
    setValue(value+1)
    setValue(value+1)
    setValue(value+1)
  }
  
  return (
    <div className="App">
      <button onClick={onClick}>+</button>
      <h1>{value}</h1>
    </div>
  );
}

문제 : 위에서 버튼을 클릭하면 어떤 값이 나올까? 3 일까?

은 1 이다.

이유

  • 이는 seState의 비동기적 특성 때문이다.
  • 리액트가 효율적으로 렌더링하기 위해 여러 개의 상태 값 변경 요청을 batch(일괄 처리) 처리하기 때문이다.
  • batch 처리의 이유는 당연히 성능 이슈 때문이다. -> 리렌더링을 발생하는 setState가 일어날 때마다 리렌더링이 일어난다면 얼마나 많은 렌더링이 일어나겠는가? 그렇다면 setState를 사용하기가 두려워질 수도 있을 듯하다.

설명

  • 용어 정의
    A : 이번 렌더링의 초기(시작) state 값
    B : 저장하여 다음 렌더링에 사용될 state 값
// A = 0

setValue(value+1)
// ① B = A + 1 => 2

setValue(value+1)
// ② B = A + 1 => 2

setValue(value+1)
// ③ B = A + 1 => 2
  • 이렇게 setState는 변경된 사항을 기억하지 않기 때문에 마지막 업데이트만 적용되어 다름 렌더링에 쓰이게 된다.

setState 함수형 업데이트

문제 : 하지만 setState 를 동기적으로 사용하는 방법은 없을까

함수형 업데이트를 사용하자!

방법

export default function App() {
  const [value, setValue] =  useState(0)

  const onClick = () => {
    setValue(prev => prev+1)
    setValue(prev => prev+1)
    setValue(prev => prev+1)
  }
  
  return (
    <div className="App">
      <button onClick={onClick}>+</button>
      <h1>{value}</h1>
    </div>
  );
}

설명

  • 용어 정의
    A : 이번 렌더링 초기(시작) state 값
    B : A 또는 이번 렌더링에서 업데이트된 값 / 다음 렌더링에 사용될 값
    C : 함수형 업데이트를 통해 생겨난 값
// A = 0
// B = A = 0

setValue(prev => prev+1)
// ①-1 : C = B + 1 => 1 
// ①-2 : C 를 B 에 저장

setValue(prev => prev+1)
// ②-1 : C = B + 1 => 2
// ②-2 : C 를 B 에 저장

setValue(prev => prev+1)
// ③-1 : C = B + 1 => 3
// ③-2 : C 를 B 에 저장
  • 함수현 업데이트는 이렇게 업데이트된 값을 저장하기 때문에 여러번의 업데이트가 적용되어 다음 렌더링에 사용된다.

함수형 업데이트를 한 상태를 다른 useState 에서 어떤 값이 사용될까?

문제 : X useState 을 함수형 업데이트하고 Y useState의 상태를 업데이트데 X state 를 사용한다면 업데이트된 X state 를 받을 수 있을까?

고민 코드

export default function App() {
  const [valueX, setValueX] =  useState(0)
  const [valueY, setValueY] =  useState(0)

  const onClick = (e) => {
    setValueX(prev => prev + 1);
    setValueX(prev => prev + 1);
    setValueY(valueX + 1)
  }

  return (
    <div className="App"  >
      <button onClick={onClick}>+</button>
      <h1>valueX : {valueX}</h1>
      <h1>valueY : {valueY}</h1>
    </div>
  );
}
  • 이 코드에서 '+'버튼을 클릭하면 valueY는 어떤 값을 보여줄까?
  • valueX 의 2가지 상태
    A : 이번 렌더링의 초기(시작) state 값
    B : 저장하여 다음 렌더링에 사용될 state 값

따라서 2가지의 경우가 나온다
case 1. A(0) + 1 = 1
case 2. B(2) + 1 = 3

  • case 1이 정답이다. valueY 는 1으로 나온다
  • Y 는 업데이트된 X의 값이 아닌 X의 이번 렌더링 초깃값을 사용하게 사용함을 의미한다.

설명

용어 정리

  • X useState -
    A : 이번 렌더링 초기(시작) state 값 = 0
    B : A 또는 이번 렌더링에서 업데이트된 값 / 다음 렌더링에 사용될 값
    C : 함수형 업데이트를 통해 생겨난 값

  • Y useState -
    D : 이번 렌더링의 초기(시작) state 값
    E : 저장하여 다음 렌더링에 사용될 state 값

setValueX(prev => prev + 1);
// ①-1 처음 setState 시 B는 A => B = 0 
// ①-2 C = B(0) + 1 = 1 
// ①-3 C 를 B에 저장
// ①-4 B = 1

setValueX(prev => prev + 1);
// ②-1 C = B(1) + 1 = 2
// ②-2 C 를 B에 저장 
// ②-3 B = 2  

setValueY(valueX + 1)
// ③-1 E = A + 1 = 1

여러 개의 useState를 업데이트했다면 과연 렌더링은 몇번 일어날까?

문제 : input에 a 를 input 에서 작성하여 1번의 onChange가 호출되었을 때 몇 번의 render 가 일어날까?

코드

export default function App() {
  const [valueX, setValueX] =  useState(0)
  const [valueY, setValueY] =  useState(0)
  const [valueZ, setValueZ] =  useState(0)

  const onChange = (e) => {
    setValueX(prev => prev + 1);
    setValueY('')
    setValueZ(e.target.value);
  }

  return (
    <div className="App"  >
     <input onChange={onChange}/>
      <h1>valueX : {valueX}</h1>
      <h1>valueY : {valueY}</h1>
      <h1>valueZ : {valueZ}</h1>
    </div>
  );
}

  • 1번의 렌더링만 일어난다.
  • setState가 호출되면 리렌더링이 되긴 하지만 그 즉시 리렌더링이 일어난 것이 아닌 JS Call Stack 이 모두 실행되고 리렌더링이 일어난다. 한꺼번에 setState들의 업데이트가 이루어지며 리렌더링 되는 것이다.
profile
Frontend Developer, who has business in mind.

0개의 댓글