react-Interaction Observer API :
어떠한 엘리먼트를 관찰하여 엘리먼트의 가시성에 대한 변화를 감지하고 그에 대한 이벤트를 발생시킬 수 있도록 도와주는 API => 어떤 변화를 감지하고 이벤트를 실행시켜주는 api라는 뜻으로 이해
비동기적으로 실행 : 메인 스레드에 영향을 주지 않으면서 변경사항을 관찰 가능. 좋은 성능의 무한 스크롤이 가능해짐.
IE를 제외하고 모두 사용 가능.
(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를 사용하면 일단 위와 같은 문제는 없어지는 것 같다.