맨 땅에 프론트엔드 개발자 되기

React state 변경 비동기 처리에 관하여 / 여러 개 state 변경 에러 해결하기 본문

코딩 공부 일지/React JS

React state 변경 비동기 처리에 관하여 / 여러 개 state 변경 에러 해결하기

헬로코딩 2022. 6. 11. 17:42
728x90

React의 state 변경 비동기 처리

React 에서 상태 변경을 할 때 setState 혹은 useState Hook을 써서 변경하면 상태 변경 동작은 비동기로 처리된다. (동기/비동기의 차이점을 알아야 밑에 내용이 이해가 가능하다.) React가 상태 변경을 비동기로 처리하는 이유는 효율성 때문이다. 리액트는 상태가 변경될 때마다 재렌더링을 일으키도록 설계되었는데, 만약 한꺼번에 너무 많은 state가 변경될 경우 일일이 재렌더링을 일으킨다면 너무 비효율적이기 때문에 state 값이 변경될 때 React 내부 로직 기준에 따라 한번에 state 변경을 취합해서 재렌더링을 일으킨다. 그리고 그 취합은 16ms 단위로 이루어진다고 한다.

여러 개 State 변경 에러 해결하기

아래 예제는 input 선택 여부에 따라 재고 개수를 하나씩 빼는 임의로 만든 예제다. 만약 과일의 개수가 너무 많아서 한꺼번에 많은 상태 변경이 일어난다면 취합이 여러 번 일어나면서 state는 객체이기 때문에 모든 상태 변경이 적용되지 않고 마지막 실행값만 덮어씌워지는 에러가 발생할 수 있다.

import { useState } from "react"

export default function Sample() {
  const [fruits, setFruits] = useState({
    apple: {selected: false, stock: 5},
    strawberry: {selected: false, stock: 2},
    banana: {selected: false, stock: 4},
    kiwi: {selected: false, stock: 6}
  });
  
  const handleClick = () => {
    fruits.apple.selected && setFruits({...fruits, apple: {...fruits.apple, stock: fruits.apple.stock - 1}});
    fruits.strawberry.selected && setFruits({...fruits, strawberry: {...fruits.strawberry, stock: fruits.strawberry.stock - 1}});
    fruits.banana.selected && setFruits({...fruits, banana: {...fruits.banana, stock: fruits.banana.stock - 1}});
    fruits.kiwi.selected && setFruits({...fruits, kiwi: {...fruits.kiwi, stock: fruits.kiwi.stock - 1}});
  };

  return (
    <section className="sample">
      <ul className="display-list">
        <li className="display-item">
          <input type="checkbox" name="apple" onChange={() => setFruits({...fruits, apple: {...fruits.apple, selected: !fruits.apple.selected}})} />
          🍎 : {fruits.apple.stock}
        </li>
        <li className="display-item">
          <input type="checkbox" name="strawberry" onChange={() => setFruits({...fruits, strawberry: {...fruits.strawberry, selected: !fruits.strawberry.selected}})} />
          🍓 : {fruits.strawberry.stock}
        </li>
        <li className="display-item">
          <input type="checkbox" name="banana" onChange={() => setFruits({...fruits, banana: {...fruits.banana, selected: !fruits.banana.selected}})} />
          🍌 : {fruits.banana.stock}
        </li>
        <li className="display-item">
          <input type="checkbox" name="kiwi" onChange={() => setFruits({...fruits, kiwi: {...fruits.kiwi, selected: !fruits.kiwi.selected}})} />
          🥝 : {fruits.kiwi.stock}
        </li>
      </ul>
      <div className="btn-wrap">
        <button onClick={handleClick}>Checkout</button>
      </div>
    </section>
  )
}

모든 상태 변경을 확실하게 적용시키기 위해서 아래와 같은 코드로 변경할 수 있다. 

import { useState } from "react"

export default function Sample() {
  const [fruits, setFruits] = useState({
    apple: {selected: false, stock: 5},
    strawberry: {selected: false, stock: 2},
    banana: {selected: false, stock: 4},
    kiwi: {selected: false, stock: 6}
  });
  
  // setState() 의 매개변수로 이전 상태값을 받아 업데이트 한다.
  const handleClick = () => {
    fruits.apple.selected && setFruits(prevState => ({...prevState, apple: {...prevState.apple, stock: prevState.apple.stock - 1}}));
    fruits.strawberry.selected && setFruits(prevState => ({...prevState, strawberry: {...prevState.strawberry, stock: prevState.strawberry.stock - 1}}));
    fruits.banana.selected && setFruits(prevState => ({...prevState, banana: {...prevState.banana, stock: prevState.banana.stock - 1}}));
    fruits.kiwi.selected && setFruits(prevState => ({...prevState, kiwi: {...prevState.kiwi, stock: prevState.kiwi.stock - 1}}));
  };

  return (
    <section className="sample">
      <ul className="display-list">
        <li className="display-item">
          <input type="checkbox" name="apple" onChange={() => setFruits({...fruits, apple: {...fruits.apple, selected: !fruits.apple.selected}})} />
          🍎 : {fruits.apple.stock}
        </li>
        <li className="display-item">
          <input type="checkbox" name="strawberry" onChange={() => setFruits({...fruits, strawberry: {...fruits.strawberry, selected: !fruits.strawberry.selected}})} />
          🍓 : {fruits.strawberry.stock}
        </li>
        <li className="display-item">
          <input type="checkbox" name="banana" onChange={() => setFruits({...fruits, banana: {...fruits.banana, selected: !fruits.banana.selected}})} />
          🍌 : {fruits.banana.stock}
        </li>
        <li className="display-item">
          <input type="checkbox" name="kiwi" onChange={() => setFruits({...fruits, kiwi: {...fruits.kiwi, selected: !fruits.kiwi.selected}})} />
          🥝 : {fruits.kiwi.stock}
        </li>
      </ul>
      <div className="btn-wrap">
        <button onClick={handleClick}>Checkout</button>
      </div>
    </section>
  )
}

setState 함수는 매개변수로 이전 상태값을 받기 때문에 이전 상태값을 받아 로직을 작성해주면 연속 상태 변경을 적용할 수 있다.

 

리액트는 공부하면 할 수록 너무 재미남😆

728x90