Skip to content
On this page

2022년 04월 27일

수정하기
문서 생성 2022-04-27 12:26:45 최근 수정 2022-04-27 23:43:08

📚 오늘 도전하고, 배운 것

알고리즘 문제

React

State와 컴포넌트

어제 궁금했던 내용. 컴포넌트의 상태가 변경될 때 컴포넌트 함수가 다시 실행되는데 왜 useState로 정의한 상태는 초기화되지 않을까? 답은 간단하다. 리액트가 그렇게 하기 때문이다. 리액트는 useState와 전달된 기본값에 대해서 한 번만 고려하도록 처리한다. 컴포넌트가 처음 렌더링될 때 useState가 실행되면 리액트가 관리하게 되는 새로운 상태 변수를 ㅁ나들게 된다. 그리고 리액트는 이 변수를 기억해두고 전달된 기본값을 사용해서 초기화한다. 이후 컴포넌트 함수가 다시 호출되어 재평가하는 과정에서 useState가 또 호출되어도 새로운 상태는 생성되지 않는다. 필요할 경우에만 상태를 업데이트 한다.

State 스케줄링과 일괄 처리에 대하여

2022년 4월 13일에 학습한 대로 useState를 이용한 상태 변경 함수를 실행한다고 해서 상태가 즉각 변경되지 않는다. 대신 예약(스케줄링)을 한다. 대부분의 경우 상태 변경이 발생하면 스케줄 작업이 매우 빠르게 발생해서 즉시 처리된 것처럼 보인다. 하지만 리액트는 즉시 처리하지 않는다. 완료되지 않은 상태 변경 작업이 여러 개가 있다면 순서가 앞선 변경 작업이 처리된 경우에 컴포넌트가 중간에 다시 렌더링된다. 하지만 아직 처리되지 않은 변경 작업은 이미 예약되어 있으므로 새로운 컴포넌트의 결과를 고려하지 못할 수 있다! 다수의 예약된 상태 변화가 동시에 있을 수 있기 때문에 함수 형태를 사용해 상태를 갱신하는 것을 추천한다. (이전 상태의 스냅샷에 의존하는 경우) 보통 상태 변경은 매우 빠르기 때문에 지연을 느끼진 못할 것이지만, 이론상으로 작업이 지연될 수도 있다. 그래서 함수를 사용하는 것이 최선이다. 또 한가지 특이한 사항이 있다. 리액트에서 상태 변경 함수가 같은 코드 조각에 존재한다면 (다른 프로미스에 있지 않고 같은 곳에 존재) 시간 지연과 같은 현상은 발생하지 않는다. 리액트는 이들에 대한 상태 갱신을 하나의 동기화 프로세스에서 같이 실행하게 된다. (batch update) 그래서 각 상태 갱신마다 렌더링을 하는 것이 아니라 재렌더링을 한 번만 한다.

useMemo

2022년 01월 14일에 궁금했던 내용을 이제 정리할 수 있게 되었다. useMemo는 리액트 훅(React Hooks) 중 하나이다.

부모 -> 자식 컴포넌트로 어떠한 배열을 전달한다고 가정해보자.

// App.js
function App() {
// ...
return (
<div className="app">
<List items={[1, 3, 9, 7, 2]} />
</div>
)
}

자식 컴포넌트에는 다음과 같이 전달된 props(배열)을 정렬하는 내용의 코드가 담겨있다.

// List.js
const List = (props) => {
const sortedList = props.items.sort((a, b) => a - b)
return (
<div>
<ul>
{sortedList.map((item) => (
<li key={item}>{item}</li>
))}
</ul>
</div>
)
}

여기서 부모 컴포넌트의 어떤 상태 하나가 변경된다면 자식 컴포넌트인 List도 다시 렌더링될 것이다. 간단한 배열이라면 괜찮지만 전달된 props의 내용이 크다면 렌더링될 때마다 정렬을 해야하니 성능에 문제가 생길 수 있다. 이때 List 컴포넌트를 React.memo로 감쌀 수 있다. 하지만 전달된 props가 배열, 즉 객체이므로 다시 생성되기 때문에 함수를 전달했을때와 같이 불필요하게 다시 렌더링된다. 이때 사용하는 것이 useMemo라는 훅이다.

useMemo는 데이터를 memoize(저장) 하는 훅이다.

// List.js
const sortedList = useMemo(() => {
return props.items.sort((a, b) => a - b)
}, [])

useEffectuseCallback과 같은 다른 훅 처럼 의존성 배열을 두 번째 인자로 받는데 예시 같은 경우에는, props.items가 변경될 때는 리턴 받는 값이 변경되어야 할 것이다. 따라서 해당 값을 다음과 같이 의존성 배열에 넣어준다.

// List.js
const { items } = props
const sortedList = useMemo(() => {
return items.sort((a, b) => a - b)
}, [items])

또한 예시에서 전달한 배열은 객체이므로 App.js 컴포넌트가 다시 렌더링될 때마다 재생성 된다. 그래서 마찬가지로 새롭게 생성된 배열이 전달되지 않도록 useMemo를 사용해준다.

// App.js
function App() {
// ...
const listItems = useMemo(() => [1, 3, 9, 7, 2], [])
return (
<div className="app">
<List items={listItems} />
</div>
)
}

🤔 학습하면서 궁금하거나 어려웠던 점

🌅 내일은 무엇을?

  • React 학습
  • 알고리즘 학습 & 문제 풀이

🖋 log

  • 지금 당장 떠오르는 것은 없지만 React.memo메모이제이션처럼 여러 분야에서 공통적으로 사용하는 용어들이 많은 것 같다. 그래서 하나만 알아도 나중에 이해에 도움이 된다고 생각한다.