[React] setState의 원리 (비동기 작동, 관련 예시) + props drilling

minjeong·2024년 9월 26일
post-thumbnail

state 리렌더

react가 리렌더하는 방식

리액트의 데이터 흐름은 단방향입니다.
즉, 부모에서 자식으로만 전달이 가능하다는 거죠.

setState비동기로 작동합니다.
why? setState가 동기로 작동하게 되면 변경될때마다 바로 바로 렌더링 하기 때문에 비효율적입니다.
값이 바뀔때마다 화면을 다시 그리게 되는 반복적인 방식은 비효율적일 수 밖에 없습니다.

따라서 임시 저장소에 모아두었다가 코드를 끝까지 읽고 한번에 바꿔서 렌더링합니다.

state 비동기 예시

(1)

주어진 값들이 존재할 경우에만, active 되게 하는 예시

const [writer, setWriter] = useState('');
const [title, setTitle] = useState('');
const [content, setContent] = useState('');
const [isActive, setIsActive] = useState(false);

const onChangeContent = (e) => {
    setContent(e.target.value); //1번

    if(writer && title && e.target.value) return setIsActive(true); // early-exit 패턴
  
  	setIsActive(false);
  }; //2번

-> 간단히 설명하자면, 1번에서는 "이러한 작업을 수행하라!"는 명령만 내려진 상태입니다. 즉, 이 시점에서는 렌더링 전이며, 1번에서 content는 아직 빈 문자열입니다. 함수가 끝나는 2번 지점까지 실행된 후에야 렌더링이 이루어지고, 그제야 화면에 반영되는 것입니다.

해당 코드에서 if(writer && title && content) 대신 if(writer && title && e.target.value)로 조건을 확인하는 이유는 setContent(e.target.value)비동기로 상태를 업데이트하기 때문입니다. 즉, setContent는 즉시 content 상태를 업데이트하지 않기 때문에, setContent 호출 후에도 content는 여전히 이전 값을 참조하고 있을 수 있습니다. 이로 인해 if(writer && title && content)를 사용하면, 최신 content 값을 반영하지 못하고, if 문이 기대한 대로 동작하지 않을 수 있습니다. 이를 방지하기 위해, content 대신 직접 입력된 값인 e.target.value를 사용하여 조건을 확인하는 것입니다.

(2)

아래의 버튼을 클릭하면 어떤 값이 도출되는지?

export default function stateTest(){
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>);
}
  • 답은 1 입니다.
  • 비동기적 특성을 가지고 있기 때문입니다.

setState 의 동작 원리 3단계

1. 비교 (Comparison)

  • setState를 호출하면, 리액트는 이전 상태와 새로운 상태를 비교하기 시작합니다.
  • 상태가 변경되었는지 확인하기 위해 얕은 비교(shallow comparison)를 수행합니다.
  • 예를 들어, 객체의 상태가 변경될 경우 리액트는 객체의 참조(reference)를 비교하여 변경 여부를 판단합니다.

2. 변경 (Update)

  • 리액트는 상태 변경이 필요하다고 판단되면, 상태를 비동기적으로 업데이트합니다.
  • 이 단계에서 상태가 즉시 변경되는 것이 아니라, 비동기 큐에 상태 변경 요청이 쌓이게 됩니다. 이로 인해 setState를 여러 번 호출하더라도 성능을 최적화하기 위해 한 번에 배치(batch)하여 업데이트합니다.
  • 이 과정은 리액트의 내부 스케줄러에 의해 관리됩니다. 이러한 비동기적 특성 때문에 setState를 호출한 직후의 상태 값은 바로 반영되지 않을 수 있습니다.

3. 렌더 (Render)

  • 상태가 변경된 후, 리액트는 해당 컴포넌트를 다시 렌더링합니다.
  • 이때, 변경된 상태값을 기반으로 새로운 UI가 만들어집니다. 리액트는 효율적으로 DOM을 업데이트하기 위해 변경된 부분만 렌더링합니다.
  • 즉, 가상 DOM(Virtual DOM)과 실제 DOM을 비교하여 변경된 부분만 실제 DOM에 적용하는 방식입니다.

📌 리렌더가 되는 상황
1. 새로운 props가 들어올 때
2. 부모 컴포넌트가 렌더링 될 때
3. 강제 업데이트(forceUpdate)가 실행될 때
4. state가 변경될 때

++ 지식 하나 더 !

props drilling

props가 자식에게 넘겨주는 단계가 두단계 이상이 될 경우를 props drilling이 일어났다고 합니다. 이는 과도라지 않으면 괜찮지만, 과도하게 이루어질 경우 해당 props가 어디서 내려지고 있는지 찾는 것이 굉장히 난해해집니다.
따라서 최대한 drilling가 일어나지 않게 해주는 것이 코드의 가독성과 유지보수 측면에서는 유리합니다. -> 방지를 위해선 global state를 이용하는 것이 좋습니다.

profile
중요한 건 꺾여도 다시 일어서는 마음

0개의 댓글