22.12.17[Infinite Scroll]

커피 내리는 그냥 사람·2022년 12월 17일
0

항해99

목록 보기
87/108

정의 참고블로그
코드 참고블로그
공식문서

Infinite Scroll 구현하기

Why?

  • react-Interaction Observer API :
    어떠한 엘리먼트를 관찰하여 엘리먼트의 가시성에 대한 변화를 감지하고 그에 대한 이벤트를 발생시킬 수 있도록 도와주는 API => 어떤 변화를 감지하고 이벤트를 실행시켜주는 api라는 뜻으로 이해

  • 비동기적으로 실행 : 메인 스레드에 영향을 주지 않으면서 변경사항을 관찰 가능. 좋은 성능의 무한 스크롤이 가능해짐.

  • IE를 제외하고 모두 사용 가능.

How?

(PostList.jsx)

...
import { useInView } from "react-intersection-observer";
...

const [page, setPage] = useState(0); //페이지수
  const [loading, setLoading] = useState(false);
  const [ref, inView] = useInView(); 
//ref는 관찰 대상을 설정
//inView는 타겟이 화면에 보이지 않으면 false, 보이면 true

  useEffect(() => {
    // 사용자가 마지막 요소를 보고 있고, 로딩 중이 아니라면
    if (inView && !loading) {
      setPage((prevState) => prevState + 1);
    }
  }, [inView, loading]);

  useEffect(() => {
    setPage(0);
    //page를 다시 0으로 초기화
    dispatch(search(""));
  }, [params]);

  useEffect(() => {
    setPage(0);
  }, [getState.searchObj]);
// getState.searchObj가 변할 때마다 페이지를 0으로 초기화 해준다.

  /**  서버에서 아이템을 가지고 오는 함수 */
  const getItems = useCallback(async () => {
    const paramCategoryObj =
      params.category === "all"
        ? params.category
        : `category/${params.category}`;

    const searchString = getState.searchObj.replace("/", "");

    const checkSearchObj =
      searchString === "" ? searchString : `/${searchString}`;

    const submitObj = {
      categoryObj: paramCategoryObj,
      pageNumberObj: page,
      pageSizeObj: 10,
      sortObj: params.sort,
      searchObj: checkSearchObj,
    };
    // 페이지로 보내는 것들에 대한 조건 obj화

    //의존하는 값(deps)들이 바뀌지 않는 한 기존 함수를 재사용할 수 있습니다.
    dispatch(__getPost(submitObj));
    // dispatch로 Slice에 payload 값 보냄
  }, [page, params, getState.searchObj]);

  // `getItems` 가 바뀔 때 마다 함수 실행
  useEffect(() => {
    getItems();
  }, [getItems]);

(PostSlice.js)


// payload 이후 처리되는 내용들
...
export const __getAddPost = createAsyncThunk(
  "posts/__getAddPost",
  async (payload, thunkAPI) => {
    try {
      const data = await Apis.getAddPostAX(payload);
      const obj = {
        getState: payload,
        data: data.data.content,
        totalElements: data.data.totalElements,
      };
      return thunkAPI.fulfillWithValue(obj);
    } catch (error) {
      return thunkAPI.rejectWithValue(error);
    }
  }
);
...

//__getAddPost
    [__getAddPost.pending]: (state) => {
      state.isLoading = true;
    },
    [__getAddPost.fulfilled]: (state, action) => {
      state.isLoading = false;
      if (action.payload.getState.pageNumberObj === 0) {
// 페이지 넘버가 0이 되면 스플라이스로 값을 초기화 후 다시 데이터를 집어 넣어준다.
        state.posts.splice(0);
        state.posts.push(...action.payload.data);
        state.postsCount = action.payload.totalElements;
        state.getState = action.payload.getState;
      } else {
        state.posts.push(...action.payload.data);
        state.postsCount = action.payload.totalElements;
        state.getState = action.payload.getState;
      }
    },

마치며...

알아보다 보니 scroll event라는 것도 있는데 이건 이벤트를 감지해 스크롤의 남은 길이를 통해 일정 길이 미만이 남을 경우 다시 데이터를 가져오는 방식이라고 한다. 근데 이 방식의 경우는 너무 많은 스크롤 이벤트가 감지될 때는 디바운스 & 스로틀이 걸려 성능 개선이 필요하다고 한다. 제일 쉽고 안전한 방법인 것인지는 모르겠으나 인터섹션 옵져버 api를 사용하면 일단 위와 같은 문제는 없어지는 것 같다.

profile
커피 내리고 향 맡는거 좋아해요. 이것 저것 공부합니다.

0개의 댓글