1.데이터1
2.데이터2
3.데이터3
4.데이터4
이런 식의 데이터를 get받아와서 list를 구현하는데 성공했다.
그 이후에 각각의 데이터를 클릭하면 그에 해당하는 상세 데이터로 이동하는 기능을 구현했다.
거기까지는 순조롭게 했지만...
각 상세 데이터 페이지로 이동하면
'이전 버튼' '목록으로 돌아가기' '다음 버튼'
이렇게 버튼이 있고 이전 버튼, 다음 버튼을 누르면 다음 상세데이터로 혹은 이전 데이터로 이동해야하는 기능을 만들어야 했다.
전체 리스트를 불러와서 사용해야겠다는 생각은 있었지만 어떻게 구현해야할지 막막해서 고민을 많이 했다.
findindex를 활용해서 구현을 시도했다.
const DetailPage = () =>{
const navigate = useNavigate();
const [searchParams] = useSearchParams();
const searchId = Number(searchParams.get(id)) || -1;
//현재 상세 데이터를 가져오기
const {data} = useGetDetail(searchId);
//전체 목록 데이터 가져오기
const [searchParam, setSearchParam] = useState<GetListParams>({
start: 0,
length:10,
searchType: 'all',
});
const {data: ListData} = useGetListData({
pageNumber: searchParam.pageNumber,
pageSize: searchParam.pageSize,
searchType: searchParam.searchType,
...
});
//이전 버튼 다음 버튼 이동하는 핸들러
const handleMove=(direction: 'prev'|'next')=>{
if(!ListData?.items) return;//데이터가 아니면 그대로 반환
const currentIndex =ListData.items.findIndex((item)=> item.id === searchId);
if(direction === 'prev' && currentIndex >0){
//이전 항목으로 이동
const prevItem = ListData.items[currentIndex -1]
if(prevItem) navigate(`상세데이터 url주소/data?id={prevItem.id}`);
}
if(direction === 'next' && currentIndex < ListData.items.length -1){
//다음 항목으로 이동
const nextItem = ListData.items[currentIndex+1];
if(nextItem) navigate('상세데이터 url주소/data?id={nextItem.id}`);
}
};
...
return(
<>
<button
onClick={()=>handleMove('prev')}
>이전</button>
<button
onClick={()=>handleMove('next')}
>다음</button>
</>
)l
};
export default DetailPage;
이렇게 하고 부픈 기대를 앉고 확인해봤지만 잘 되지 않았다....
상세페이지의 데이터를 가져오는 useGetDetail는 React의 hook으로 동작하는 함수이다.
React의 규칙상, hook은 반드시 컴포넌트 최상위에서 호출되어야 한다.
그러므로, if문 안에서 호출하는 방식은 옳지 않다고 한다.
react는 훅 호출 순서를 기준으로 상태와 효과를 연결한다.
즉 훅이 호출되는 순서와 위치가 매우 중요하다!
React는 훅의 의존성 배열을 사용하여 상태 변경 및 리렌더링을 최적화한다.
만약 조건문 안에서 훅이 호출되면, 의존성 배열을 관리하는 로직이 깨지게 되어 예기치 않은 동작이 발생할 수 있다.
React는 훅 호출을 함수 호출 스택과 유사하게 관리한다.
결론적으로 React 훅은 컴포넌트의 최상위에서만 호출하여 호출 순서를 일관되게 유지하는게 중요하다!!!
그래야 React의 상태 관리 매커니즘과 의존성 추적 로직이 깨지지 않게 된다!
const DetailPage = ()=>{
const navigate = useNavigate();
const [searchParams] = useSearchParams();
const initialId = Number(searchParams.get('id')) || -1;
// 현재 상세 데이터의 ID 상태
const [currentId, setCurrentId] = useState(initialId);
// 현재 상세 데이터를 가져오기
const { data } = useGetNoticeDetail(currentId);
//리스트 데이터 가져오기
const [searchParam, setSearchParam] = useState<GetListParams>({
start: 0,
length:10,
searchType: 'all',
});
const {data: ListData} = useGetListData({
pageNumber: searchParam.pageNumber,
pageSize: searchParam.pageSize,
searchType: searchParam.searchType,
...
});
//이전 다음 항목 이동 핸들러
const handleMove = (direction: 'prev' | 'next') => {
if (!ListData?.items) return;
const currentIndex = ListData.items.findIndex((item) => item.id === currentId);
if (direction === 'prev' && currentIndex > 0) {
// 이전 항목으로 이동
const prevItem = ListData.items[currentIndex - 1];
if (prevItem) setCurrentId(prevItem.id);
}
if (direction === 'next' && currentIndex < ListData.items.length - 1) {
// 다음 항목으로 이동
const nextItem = ListData.items[currentIndex + 1];
if (nextItem) setCurrentId(nextItem.id);
}
};
return(
<>
<button
onClick={()=>handleMove('prev')}
>이전</button>
<button
onClick={()=>handleMove('next')}
>다음</button>
</>
)l
};
export default DetailPage;
이렇게 id로 상세 데이터를 받아오기 때문에 그 id를 currentId로 설정해 state로 관리하였다.
그 전에는 const {data} = useGetDetail(searchId);이렇게 searchId의 값이 훅의 동작 조건을 변화시킬 수 있었기에 최상위에 위치해도 잘 동작하지 않았다.
그렇기에 데이터를 가져올 때 사용되는 id를 state로 관리하면서 const { data } = useGetNoticeDetail(currentId); 불로오는 방식으로 바꾸었다.