이 글은 노마드 코더님의 React 강의를 학습한 내용을 기반으로 작성합니다.
Introduction
우리는 Component가 처음 render될 때만 코드가 실행되길 원할 수 있다.
즉, 첫 번째 render에만 코드가 실행되고, 다른 state 변화에서는 실행되지 않도록 하고 싶다.
예를 들어, API를 통해 데이터를 가져올 때 첫 번째 component render에서 API를 호출하고 이후 state가 변화할 때는 해당 데이터를 다시 가져오지 않아도 되는 경우가 존재한다.
예제 코드
import { useState } from "react";
function App() {
const [counter, setValue] = useState(0);
const onClick = () => setValue((prev) => prev + 1);
console.log("call an api");
return (
<div>
<h1>{counter}</h1>
<button onClick={onClick}>Click me!</button>
</div>
);
}
export default App;
결과 화면
초기에 call an api가 한 번 호출된 후 해당 컴포넌트의 state가 변경될 때마다 api가 호출되는 것을 확인할 수 있다.
이때, state값이 변경되어도 api가 호출되지 않도록 하는 것이 목표이다.
useEffect
useEffect란 두 개의 argument를 가지는 function이다.
- 한 번만 실행하고 싶은 코드
- Dependency
사용 예시
import { useEffect, useState } from "react";
function App() {
const [counter, setValue] = useState(0);
const onClick = () => setValue((prev) => prev + 1);
console.log("I run all the time");
const iRunOnlyOnce = () => {
console.log("i run only once.");
};
useEffect(iRunOnlyOnce, []);
return (
<div>
<h1>{counter}</h1>
<button onClick={onClick}>Click me!</button>
</div>
);
}
export default App;
결과 화면
결과를 확인해 보면 iRunOnlyOnce 함수의 경우 해당 단 한 번만 실행된 것을 알 수 있다.
Deps
예제 코드
import { useEffect, useState } from "react";
function App() {
const [counter, setValue] = useState(0);
const [keyword, setKeyword] = useState("");
const onClick = () => setValue((prev) => prev + 1);
const onChange = (event) => setKeyword(event.target.value);
console.log("I run all the time");
const iRunOnlyOnce = () => {
console.log("CALL THE API....");
};
console.log("SEARCH FOR", keyword);
useEffect(iRunOnlyOnce, []);
return (
<div>
<input
value={keyword}
onChange={onChange}
type="text"
placeholder="Search here..."
/>
<h1>{counter}</h1>
<button onClick={onClick}>Click me!</button>
</div>
);
}
export default App;
결과 화면
keyword 값이 변화하거나 버튼이 클릭될 때마다 "SEARCH FOR" 가 출력되는 것을 확인할 수 있다.
예제에서 살펴볼 내용은 keyword가 변화하는 경우에만 검색이 가능하도록 하는 것이다.
(= counter가 변경될 때는 검색이 되지 않도록 한다.)
문제 해결 방법
두 번째 파라미터 배열 내부에 'keyword'를 작성한다.
그 결과, 해당 코드는 keyword 값이 변경되는 경우에만 실행된다.
코드
import { useEffect, useState } from "react";
function App() {
const [counter, setValue] = useState(0);
const [keyword, setKeyword] = useState("");
const onClick = () => setValue((prev) => prev + 1);
const onChange = (event) => setKeyword(event.target.value);
console.log("I run all the time");
useEffect(() => {
console.log("CALL THE API....");
}, []);
useEffect(() => {
if (keyword !== "" && keyword.length > 5) {
console.log("SEARCH FOR", keyword);
}
}, [keyword]);
return (
<div>
<input
value={keyword}
onChange={onChange}
type="text"
placeholder="Search here..."
/>
<h1>{counter}</h1>
<button onClick={onClick}>Click me!</button>
</div>
);
}
export default App;
결과 화면
Cleanup
예제 코드
import { useState, useEffect } from "react";
function Hello() {
useEffect(() => {
console.log("I'm here");
});
return <h1>Hello</h1>;
}
function App() {
const [showing, setShowing] = useState(false);
const onClick = () => setShowing((prev) => !prev);
return (
<div>
{showing ? <Hello /> : null}
<button onClick={onClick}>{showing ? "Hide" : "Show"}</button>
</div>
);
}
export default App;
결과 화면
console.log("I'm here");를 useEffect 방식을 사용해 호출했음에도 불구하고 여러 번 출력되는 것을 확인할 수 있다.
이와 같은 상황이 발생하는 이유는 Hello 컴포넌트를 screen에서 지워고 그리는 작업을 반복하기 때문이다.
(주의! Hello 컴포넌트를 숨기는 것이 아닌 지우는 것이다.)
Clean-up
React.js에서는 또한 컴포넌트가 destroy 될 때도 코드를 실행할 수 있도록 지원해준다.
예제 코드
import { useState, useEffect } from "react";
function Hello() {
useEffect(() => {
console.log("Created");
return () => console.log("Destryoed");
}, []);
return <h1>Hello</h1>;
}
function App() {
const [showing, setShowing] = useState(false);
const onClick = () => setShowing((prev) => !prev);
return (
<div>
{showing ? <Hello /> : null}
<button onClick={onClick}>{showing ? "Hide" : "Show"}</button>
</div>
);
}
export default App;
결과 화면
이런 것을 바로 Cleanup function이라 한다.
단순한 function인데 컴포넌트가 destroy될 때 특정 기능을 수행할 수 있도록 지원해준다.
또한, 위 코드를 아래와 같이 변경할 수 있다.
import { useState, useEffect } from "react";
function Hello() {
function byFn() {
console.log("Destryoed");
}
function hiFn() {
console.log("Created");
return byFn;
}
useEffect(hiFn, []);
return <h1>Hello</h1>;
}
function App() {
const [showing, setShowing] = useState(false);
const onClick = () => setShowing((prev) => !prev);
return (
<div>
{showing ? <Hello /> : null}
<button onClick={onClick}>{showing ? "Hide" : "Show"}</button>
</div>
);
}
export default App;
조금 길어보이긴 하지만 결국 똑같다.
useEffect는 dependency가 변화할 때 호출된다.
하지만 이 경우 dependency가 비어있으니 컴포넌트가 처음 생성될 때 function이 호출되고 다시는 호출되지 않는다.
컴포넌트가 지워질 때도 특정 함수를 실행하고 싶다면 hiFn이 byFn을 반환해야 한다.
이때, React는 컴포넌트가 생성될 때 hiFn을 실행하고 컴포넌트가 파괴될 때는 hiFn이 반환한 byFn을 실행시킬 것이다.