더북(TheBook)

9.5.7 커스텀 Hook을 작성해 컴포넌트 리팩토링하기

posts.js 파일에서 Firestore에 포스트를 조회하는 함수를 작성할 때 함수가 비슷해지자 getPosts를 재사용할 수 있는 형태로 리팩토링해줬습니다. 만약 컴포넌트에서 비슷한 UI가 반복된다면 컴포넌트를 분리해 재사용하면 되는데요. 이때 컴포넌트의 로직만 반복된다면 어떻게 할까요? 그럴 때는 커스텀(Custom) Hook을 만들어서 로직을 재사용할 수 있습니다.

프로젝트의 루트 디렉터리에 hooks 디렉터리를 만들고 그 안에 usePosts라는 함수를 다음과 같이 작성해보세요. 기존에 만든 Profile 컴포넌트에서 필요한 코드를 가져와 붙여넣으면 됩니다.

hooks/usePosts.js

import {useEffect, useState} from 'react';
import {getNewerPosts, getOlderPosts, getPosts, PAGE_SIZE} from '../lib/posts';

export default function usePosts(userId) {
  const [posts, setPosts] = useState(null);
  const [noMorePost, setNoMorePost] = useState(false);
  const [refreshing, setRefreshing] = useState(false);

  const onLoadMore = async () => {
    if (noMorePost || !posts || posts.length < PAGE_SIZE) {
      return;
    }
    const lastPost = posts[posts.length - 1];
    const olderPosts = await getOlderPosts(lastPost.id, userId);
    if (olderPosts.length < PAGE_SIZE) {
      setNoMorePost(true);
    }
    setPosts(posts.concat(olderPosts));
  };

  const onRefresh = async () => {
    if (!posts || posts.length === 0 || refreshing) {
      return;
    }
    const firstPost = posts[0];
    setRefreshing(true);
    const newerPosts = await getNewerPosts(firstPost.id, userId);
    setRefreshing(false);
    if (newerPosts.length === 0) {
      return;
    }
    setPosts(newerPosts.concat(posts));
  };

  useEffect(() => {
    getPosts({userId}).then((_posts) => {
      setPosts(_posts);
      if (_posts.length < PAGE_SIZE) {
        setNoMorePost(true);
      }
    });
  }, [userId]);

  return {
    posts,
    noMorePost,
    refreshing,
    onLoadMore,
    onRefresh,
  };
}
신간 소식 구독하기
뉴스레터에 가입하시고 이메일로 신간 소식을 받아 보세요.