개인 학습용 기록입니다. 틀린 부분이 있을 수 있습니다! 혹시 있다면 댓글 부탁드립니다. 🙇♂️
const todos: NextPage = () => {
const [todos, setTodos] = useState<TypeTodo[]>(null);
const [isLoading, setIsLoading] = useState<boolean>(false);
const fetchTodos = async () => {
try {
setIsLoading(true);
const { data } = await todoApi.loadTodos();
setTodos(data);
setIsLoading(false);
} catch (e) {
console.log(e);
setIsLoading(false);
}
};
useEffect(() => {
fetchTodos();
}, []);
if (isLoading) return <div>로딩 중!</div>;
return (
<div>
{todos &&
todos.slice(0, 20).map((todo) => (
<Link href={`/todos/${todo.id}`} key={todo.id}>
<a>
<TodoRow>{todo.title}</TodoRow>
</a>
</Link>
))}
</div>
);
};
export default todos;
SWR
를 사용하지 않은 경우, react hook
을 이용해서 상태를 관리했다. useEffect
를 이용해서 컴포넌트가 mount될 때 fetchTodos()
를 실행하게 했으며, useState
를 이용해서 값을 할당했다.
또한, data
가 fetch하는 상태를 관리하기 위해 isLoading
이라는 상태를 또 하나 만들었으며, 로딩상태에 따라 early return
문을 통해 error를 방지했다.
redux
나 context api
등과 같은 전역 상태관리가 적용됐다면 코드가 달라질수도 있겠지만 비동기 처리 로직의 위치가 다른 곳으로 바뀌는 것일뿐 아마 일반적인 과정은 위와 같을 것 같다.
const todos: NextPage = () => {
const fetcher = async (url: string) => {
const { data } = await todoApi.loadTodos(url);
return data;
};
const { data: todos, error } = useSWR<TypeTodo[]>("/todos", fetcher);
if (!todos) return <div>로딩 중!</div>;
return (
<div>
{todos &&
todos.slice(0, 20).map((todo) => (
<Link href={`/todos/${todo.id}`} key={todo.id}>
<a>
<TodoRow>{todo.title}</TodoRow>
</a>
</Link>
))}
</div>
);
};
export default todos;
SWR
를 사용하면, 일단 useState
와 useEffect
가 사라진다. useSWR
는 컴포넌트가 mount될 때 실행되기 때문에 useEffect
가 필요없다. 또한 이것을 굳이 어떤 값에 할당하지 않아도 되며, !data
를 통해 loading
상태를 유추 할 수 있다.
만약 data : todos
라는 값을 다른 컴포넌트에서 사용하고자 한다면, 즉 전역으로 관리하고 싶다면 그 컴포넌트에서도 useSWR("/todos", fetcher)
를 하면 된다.
그렇게 하면 API 요청을 중복으로 하게 되잖아요?
SWR은 중복으로 요청안하고 1번만 한다. 아마 이게 SWR의 진가가 아닐까 생각한다. end-point
를 기준으로 동일한 요청이 있다면 최초에 한번만 요청을 보내고, 이후에는 캐싱해 놓은 결과값을 사용하여 클라이언트에 보여준다. 즉, useSWR
을 여러 개의 컴포넌트에서 아무리 작동시켜도 end-point
만 같다면 1번만 요청을 보낸다. 이러한 원리로 redux
의 대체자 라고 불리고 있는 것 같다.
로컬에서만 관리하든, 전역으로 관리하든 코드가 줄고 리덕스보다 쉽다. 물론 mutate
같은 것들이 더 추가 되어야 하지만 말이다.