Effect는 외부 시스템과 동기화할 때 쓰는 탈출구(Escape Hatch)다. 렌더 중에 계산할 수 있는 값이나 사용자 이벤트 처리에 Effect를 쓰면 렌더가 한 번 더 일어나고 흐름이 꼬인다. React 공식 문서가 이런 경우를 따로 정리해 두었다.
- props·state에서 계산되는 값은 그냥 렌더 중에 계산한다. 별도 state와 Effect로 동기화하지 않는다. 비싼 계산이면
useMemo로 캐시한다(정말 비싼지는console.time으로 잰다) - prop이 바뀔 때 state 전체를 초기화하려면 컴포넌트에 다른
key를 준다(재조정(Reconciliation)) - 일부 state만 조정하려면 이전 값을 state에 저장해 두고 렌더 중에 비교한다
const [prevCount, setPrevCount] = useState(count)
if (prevCount !== count) { // 렌더 중 자기 state 갱신
setPrevCount(count)
setTrend(count > prevCount ? 'up' : 'down')
}React는 반환된 JSX(JavaScript XML)를 버리고 바로 다시 렌더한다. 조건이 있어 한 번에 수렴하고, 지금 렌더 중인 컴포넌트 자신의 state만 고치기 때문에 허용된다. 부모나 다른 컴포넌트의 state를 고치면 안 된다. React 19의 새 기능이 아니라 오래전부터 가능했던 동작이고, Effect 남용을 줄이자는 흐름에서 다시 주목받았다
- 사용자 이벤트 때문에 일어나는 일은 이벤트 핸들러에 둔다. "이 코드가 왜 실행돼야 하나?"를 묻는다. 화면에 보였기 때문이면 Effect, 사용자가 무언가 했기 때문이면 핸들러다
- Effect끼리 state를 연쇄로 갱신하면 단계마다 렌더가 반복된다. 한 이벤트 안에서 한 번에 계산한다
- 여러 컴포넌트의 state를 맞추려 한다면 state를 위로 올린다(Lifting State Up)(React 상태 갱신)
- Effect로 데이터를 가져온다면 정리 함수로 경쟁 상태(Race Condition)를 막는다. 보통은 TanStack Query나 서버 컴포넌트가 더 낫다
배경은 부수 효과와 Effect, 최신 값을 읽되 재실행은 막는 도구는 useEffectEvent.