TIL: React의 의존성 관리
재밌는 질문을 받았다. 그리고 답변을 고민하다가 React가 의존성을 관리하는 방식에 대해 알게 되어서 글로 남긴다.
질문
질문은 아래 컴포넌트를 렌더링하고 버튼을 2번째 눌렀을 때 왜 저 함수가 다시 호출되면서 console.log
가 실행되는지 모르겠다는 내용이었다.
import { useEffect, useState } from "react";
function UseEffectTest() {
// state only for rerender
const [foo, setFoo] = useState(true);
console.log("outside of useEffect, but inside of component", "foo: ", foo);
useEffect(() => {
console.log("RE-RENDERED", "foo: ", foo);
});
return (
<div>
{console.log("---inside JSX---", "foo: ", foo)}
<button
onClick={() => {
setFoo(false);
}}
>
rerender me!
</button>
</div>
);
}
export default UseEffectTest;
나도 모르던 내용이라 처음엔
스트릭트 모드 때문 아닌가?
했다. 그런데 프로덕션 빌드에서도, next@12
와 react@17
로 테스트해봐도 똑같더라.
그리고 버튼을 더 눌러도 다시 렌더링이 되지 않았다.
고민하다가 깨달았다. 이건 React API의 한계다.
답변
React 함수 컴포넌트는 의존성 배열이라는 걸 사용한다.
그리고 foo
는 boolean
타입을 가진 변수인데, 의존성 배열에 들어갈 수 있다.
변화 내용을 추적하거나 의존성 그래프를 그리기 위한 정보가 아예 없는, boolean
같은 값들이 의존성 배열에 직접 들어간다는 것이다.
use*
는 의존성 그래프 없이 다시 실행할지 여부를 결정해야한다.
이럴 때 쓸 수 있는 방법이 foo
를 false
로 놓고 컴포넌트를 다시 실행해보는 것이다.
실행하면서 의존성 배열에 다른 값이 들어오는지 확인하면, foo
가 false
로 바뀌었을 때 다시 실행돼야하는 코드가 있는지 알 수 있다.
이후에 버튼을 다시 클릭해도 로그가 찍히지 않는 건 이전 실행의 결과로 foo
가 false
로 바뀌어도 다시 실행될 필요가 없다는 걸 알았기 때문일 것이다.
오랜만에 답변하면서도 배울 게 있는 질문이었다.