* 이 글은 React useEffect: 4 Tips Every Developer Should know 번역하였습니다.
React hooks에 useEffect의 대하여 이야기해볼까요. 절대로 잊어버리면 안 될 4가지 팁을 공유드립니다.
Use a useEffect for a SIngle purpose
React Hook을 사용할 때 복수의 useEffect 함수를 사용할 수 있습니다. 하지만 그렇게 좋은 것만은 아닙니다. 클린코드의 관점에서는 함수는 한가지 목적을 가지고 있어야 하기 때문입니다.
useEffect를 단순한 목적의 함수로 분리한다면 의도하지 않은 useEffect함수의 실행을 예방할 수 있습니다. 물론 Dependency 배열도 사용해야 합니다.
먼저 useEffect를 이용해 재귀적인 카운터를 구현하는 나쁜 코드를 작성해봅시다. 내부에서는 서로 관련이 없는 varA와 varB 변수를 사용합니다.
function App() {
const [varA, setVarA] = useState(0);
const [varB, setVarB] = useState(0);
// 이렇게 하면 안된다!
useEffect(() => {
const timeoutA = setTimeout(() => setVarA(varA + 1), 1000);
const timeoutB = setTimeout(() => setVarB(varB + 2), 2000);
return () => {
clearTimeout(timeoutA);
clearTimeout(timeoutB);
};
}, [varA, varB]);
return (
<span>
Var A: {varA}, Var B: {varB}
</span>
);
}
한번의 상태 변경으로 varA와 varB 두 상태 변수에 업데이트가 발생하게 됩니다. 결론적으로 보이는 hook은 정상적으로 동작하지 않습니다.
위에 예시는 짧은 코드이기 때문에 문제점을 찾을 수 있지만, 만약 더 길고 복잡한 코드였다면 이러한 문제를 찾기 힘들수도 있습니다.
이러한 문제를 해결하기 위해서는 useEffect를 분리하는 것입니다. 아래와 같은 코드로 말이죠
function App() {
const [varA, setVarA] = useState(0);
const [varB, setVarB] = useState(0);
useEffect(() => {
const timeout = setTimeout(() => setVarA(varA + 1), 1000);
return () => clearTimeout(timeout);
}, [varA]);
useEffect(() => {
const timeout = setTimeout(() => setVarB(varB + 2), 2000);
return () => clearTimeout(timeout);
}, [varB]);
return (
<span>
Var A: {varA}, Var B: {varB}
</span>
);
}
Use custom hooks if possible
위에 예제를 다시 살펴보았을 때 만약 varA와 varB가 완전히 독립적이었으면 어땟을까요?
Custom hook을 만든다면 두 state의 변수를 완전히 독립적으로 만들 수 있습니다. 또한 어떤 변수를 사용하는지 쉽게 파악할 수 있습니다.
아래는 Custom hook으로 만든 코드입니다.
function App() {
const [varA, setVarA] = useVarA();
const [varB, setVarB] = useVarB();
return (
<span>
Var A: {varA}, Var B: {varB}
</span>
);
}
function useVarA() {
const [varA, setVarA] = useState(0);
useEffect(() => {
const timeout = setTimeout(() => setVarA(varA + 1), 1000);
return () => clearTimeout(timeout);
}, [varA]);
return [varA, setVarA];
}
function useVarB() {
const [varB, setVarB] = useState(0);
useEffect(() => {
const timeout = setTimeout(() => setVarB(varB + 2), 2000);
return () => clearTimeout(timeout);
}, [varB]);
return [varB, setVarB];
}
각각의 변수들은 고유의 hook을 가지게 되어 유지보수가 더 쉽고 이해하기가 더 쉬워졌습니다.
The right way of conditional useEffect
setTimeout를 사용했던 예시에서 다음의 코드를 살펴봅시다.
function App() {
const [varA, setVarA] = useState(0);
useEffect(() => {
const timeout = setTimeout(() => setVarA(varA + 1), 1000);
return () => clearTimeout(timeout);
}, [varA]);
return <span>Var A: {varA}</span>;
}
Counter를 5개까지 제한한다고 했을 때, 옳은 방법과 잘못된 방법을 살펴봅시다.
잘못된 방법은 다음과 같습니다.
function App() {
const [varA, setVarA] = useState(0);
useEffect(() => {
let timeout;
if (varA < 5) {
timeout = setTimeout(() => setVarA(varA + 1), 1000);
}
return () => clearTimeout(timeout);
}, [varA]);
return <span>Var A: {varA}</span>;
}
위에 코드는 정상적으로 동작하지만, setTimeout은 조건적으로 실행되는 반면 clearTimeout은 varA가 변경될 때마다 매번 실행이 될 것입니다.
useEffect를 조건적으로 사용하는 좋은 방법은 함수 초기에 즉시 반환하는 것입니다. 아래의 코드에서 살펴보시죠.
function App() {
const [varA, setVarA] = useState(0);
useEffect(() => {
if (varA >= 5) return;
const timeout = setTimeout(() => setVarA(varA + 1), 1000);
return () => clearTimeout(timeout);
}, [varA]);
return <span>Var A: {varA}</span>;
}
이 방법은 많은 곳에서 사용되고 있으며, 의도치 않은 useEffect의 실행을 막을 수 있습니다.
Add all variables used in useEffect to the dependency array
ESLint를 사용한다면 exhaustive-deps의 경고를 본적이 있을 것입니다.
이는 매우 중요합니다. application이 커질수록 useEffect에는 더 많은 dependency들이 추가될 수 있지만, 클로저가 제대로 작동하는 것을 만들기 위해서는 모든 dependency들을 dependency 배열에 추가해주어야 합니다.
위에 작성하였던 setTimeout 예시를 재 사용해보죠. 여기서 setTimeout을 한 번만 실행하여 varA를 증가시킨다고 가정해 봅시다. 아래의 코드와 잘 못 짜여진 코드입니다.
function App() {
const [varA, setVarA] = useState(0);
useEffect(() => {
const timeout = setTimeout(() => setVarA(varA + 1), 1000);
return () => clearTimeout(timeout);
}, []);
return <span>Var A: {varA}</span>;
}
위 코드는 동작할 것입니다. 그러나 만약 코드가 더 많아지거나 코드를 변경해야 할 때를 생각해보세요.
이러한 상황에 대비하여 사용되는 모든 변수들은 dependency 배열에 모두 추가해야 합니다. 테스트를 쉽게 할 수 있고, 혹시 발생할 문제를 쉽게 탐지할 수 있게 만듭니다.
잘 짜여진 코드는 다음과 같습니다.
function App() {
const [varA, setVarA] = useState(0);
useEffect(() => {
if (varA > 0) return;
const timeout = setTimeout(() => setVarA(varA + 1), 1000);
return () => clearTimeout(timeout);
}, [varA]);
return <span>Var A: {varA}</span>;
}
긴 글 읽어주셔서 감사합니다.
< 참고자료 >
[사이트] #Midum
medium.com/swlh/useeffect-4-tips-every-developer-should-know-54b188b14d9c
<React> React useEffect: 4 Tips Every Developer Should Know end
'Language & Framework & Library > React' 카테고리의 다른 글
react useState에 대한 공통적인 실수 (1) | 2024.03.17 |
---|---|
React Render Props and HOC 이해하기 (0) | 2023.11.26 |
React Hooks의 커다란 빙산 (6) | 2023.11.17 |
Function component와 Class component는 어떻게 다를까? (1) | 2023.11.04 |
useEffect 완벽 가이드 2편 (1) | 2023.10.29 |