[React] Effect Hook - 2
2022.12.06
💡 Effect Hook -2
📌 Effect Hook 조건부 실행
조건부 effect 발생 ( dependency array )
useEffect의 두 번째 인자는 배열이다. 이 배열은 boolean형태의 표현식이 아닌, 어떤 값의 변경이 일어날 때의 조건을 의미한다.
따라서 해당 배열엔 어떤 값의 목록이 들어간다. 이 배열을 특별히 종속성 배열이라고 부른다.
import { useEffect, useState } from "react";
import "./styles.css";
import { getProverbs } from "./storageUtil";
export default function App() {
const [proverbs, setProverbs] = useState([]);
const [filter, setFilter] = useState("");
const [count, setCount] = useState(0);
useEffect(() => {
console.log("언제 effect 함수가 불릴까요?");
const result = getProverbs(filter);
setProverbs(result);
}, [filter]);
const handleChange = (e) => {
setFilter(e.target.value);
};
const handleCounterClick = () => {
setCount(count + 1);
};
return (
<div className="App">
필터
<input type="text" value={filter} onChange={handleChange} />
<ul>
{proverbs.map((prvb, i) => (
<Proverb saying={prvb} key={i} />
))}
</ul>
<button onClick={handleCounterClick}>카운터 값: {count}</button>
</div>
);
}
function Proverb({ saying }) {
return <li>{saying}</li>;
}
이 예제는, filter
가 변할 때에만, effect 함수가 실행된다. 한편, 카운트를 올리는 버튼은 컴포넌트의 상태가 바뀌고 업데이트되지만, 아무리 버튼을 눌러도 effect 함수는 실행되지 않는다. 왜냐하면, 종속성 배열에는 filter
만 존재하고, count
는 존재하지 않기 때문
종합해서 이야기하면
useEffect(함수, [종속성1, 종속성2, …])
useEffect
의 두 번째 인자는 종속성 배열이다. 배열 내의 종속성1, 또는 종속성2의 값이 변할 때, 첫 번째 인자의 함수가 실행된다.
배열 내의 어떤 값이 변할 때에만, (effect가 발생하는) 함수가 실행된다.
단 한 번만 실행되는 Effect 함수
만일 종속성 목록에 아무런 종속성도 없다면 어떤 일이 발생할까? 달리 말해, 두 번째 배열을 빈 배열 [ ]
로 둘 경우에는 무슨 일이 발생할까? 두 번째 인자를 아예 안 넘기는 것과 어떻게 다를까?
빈 배열 넣기
useEffect( 함수, [] )
아무것도 넣지 않기 ( 기본 형태 )
useEffect( 함수 )
2번 기본 형태의 useEffect는 컴포넌트가 처음 생성되거나, props가 업데이트되거나, state가 업데이트될 때 effect 함수가 실행됨을 앞서 배웠다.
반면 1번 빈 배열을 useEffect의 두 번째 인자로 사용하면, 이때에는 컴포넌트가 처음 생성될 때만 effect 함수가 실행된다. 이것이 언제 필요할까?
대표적으로 처음 단 한 번, 외부 API를 통해 리소스를 받아오고 더 이상 API 호출이 필요하지 않을 때 사용할 수 있다.
📌 컴포넌트 내에서의 Ajax 요청
목록 내 필터링을 구현하기 위해서는 다음과 같은 두 가지 접근이 있다.
- 컴포넌트 내에서 필터링: 전체 목록 데이터를 불러오고, 목록을 검색어로 filter 하는 방법
- 컴포넌트 외부에서 필터링: 컴포넌트 외부로 API 요청을 할 때, 필터링 한 결과를 받아오는 방법 (보통, 서버에 매번 검색어와 함께 요청하는 경우가 이에 해당)
각각의 장단점은 무엇일까? 지금은, 우리가 storageUtil.js
를 이용해 외부 API를 직접 구현했지만(LocalStorage API를 이용), 이는 서버 요청으로 대체할 수 있다. 만일 서버에서 수십만 개의 명언을 제공한다고 가정해 보자. 다음의 표는 HTTP를 이용한 서버 요청을 가정할 때, 두 방식의 차이점을 설명한다.
Ajax요청을 보내보자
useEffect(() => {
fetch(`http://서버주소/proverbs?q=${filter}`)
.then(resp => resp.json())
.then(result => {
setProverbs(result);
});
}, [filter]);
Ajax 요청이 매우 느릴 경우?
모든 네트워크 요청이 항상 즉각적인 응답을 가져다주는 것은 아니다. 외부 API 접속이 느릴 경우를 고려하여 로딩페이지의 구현은 필수적이다.
const [isLoading, setIsLoading] = useState(true);
// 생략, LoadingIndicator 컴포넌트는 별도로 구현했음을 가정합니다
return {isLoading ? <LoadingIndicator /> : <div>로딩 완료 화면</div>}
fetch 요청 전후로 setIsLoading을 설정해주어 보다 나은 UX를 구현할 수 있다.
useEffect(() => {
setIsLoading(true);
fetch(`http://서버주소/proverbs?q=${filter}`)
.then(resp => resp.json())
.then(result => {
setProverbs(result);
setIsLoading(false);
});
}, [filter]);
Comments