React
useInfiniteQuery 사용하기
Mori_FEDev
2023. 10. 11. 13:57
import React, { useContext, useEffect, useRef, useState } from 'react';
import { LocalStateContext } from '../../api/LocalStateContext';
import { DownChevronSvg } from '../svgs/ButtonSvg';
import { useInfiniteQuery } from '@tanstack/react-query';
import { useAxiosAuth } from '../../api';
import { Tooltip as ReactTooltip } from 'react-tooltip';
const ImgSlide = ({ openState, setOpenState }: any) => {
const { imgState } = useContext(LocalStateContext);
const [isFullScreen, setIsFullScreen] = useState(false);
const [fullScreenData, setFullScreenData] = useState(null);
const axiosAuth = useAxiosAuth();
const fetchThumbData = async (pageParam: number) => {
const response = await axiosAuth.get(
`/aidelete/files/thumbURL?rootNodeId=${imgState?.rootNodeId}&selectedNodeId=${imgState?.structureId}&ancestorId=${imgState?.ancestorId}&depth=${imgState?.depth}&division=${imgState?.category}&page=${pageParam}`,
);
//return response해야 올바른값 옴
return response;
};
enum ThumbData {
ThumbData = 'thumbData',
}
// useInfiniteQuery를 사용하여 데이터를 가져오고 페이지네이션을 설정합니다.
const {
data,
isLoading: isThumbLoading,
refetch,
fetchNextPage,
hasNextPage,
isFetchingNextPage,
} = useInfiniteQuery([ThumbData, ThumbData], ({ pageParam = 1 }) => fetchThumbData(pageParam), {
getNextPageParam: (lastPage, allPages) => {
// @ts-ignore
return lastPage.currentPage < lastPage.totalPage ? allPages.length + 1 : undefined;
},
});
useEffect(() => {
refetch();
}, [imgState]);
const [i, setI] = useState(2);
const scrollContainerRef = useRef<HTMLDivElement | null>(null);
const originImgScrollContainerRef = useRef<HTMLDivElement | null>(null);
const anonyImgScrollContainerRef = useRef<HTMLDivElement | null>(null);
// imgState가 변경될 때 스크롤 위치를 초기화
const resetScrollPosition = () => {
if (scrollContainerRef.current) {
scrollContainerRef.current.scrollTo({ left: 0, behavior: 'auto' });
}
if (originImgScrollContainerRef.current) {
originImgScrollContainerRef.current.scrollTo({ left: 0, behavior: 'auto' });
}
if (anonyImgScrollContainerRef.current) {
anonyImgScrollContainerRef.current.scrollTo({ left: 0, behavior: 'auto' });
}
setI(2);
};
useEffect(() => {
// imgState가 변경될 때 스크롤 위치를 초기화
resetScrollPosition();
}, [imgState]);
const handleScroll = () => {
const scrollContainer = scrollContainerRef.current;
if (scrollContainer) {
const scrollLeft = scrollContainer.scrollLeft;
const scrollWidth = scrollContainer.scrollWidth;
// 다른 컴포넌트에 스크롤 위치 설정
if (originImgScrollContainerRef.current) {
// 원본 이미지 컴포넌트의 스크롤 위치 설정
originImgScrollContainerRef.current.scrollLeft = scrollLeft;
}
if (anonyImgScrollContainerRef.current) {
// 익명화 이미지 컴포넌트의 스크롤 위치 설정
anonyImgScrollContainerRef.current.scrollLeft = scrollLeft;
}
if (scrollLeft + scrollContainer.clientWidth >= scrollWidth) {
// 스크롤이 오른쪽 끝에 도달하면 다음 페이지를 가져옵니다.
setI((prevI) => prevI + 1);
fetchNextPage({ pageParam: i });
}
}
};
useEffect(() => {
const scrollContainer = scrollContainerRef.current;
if (scrollContainer) {
//
scrollContainer.addEventListener('scroll', handleScroll);
return () => {
//
scrollContainer.removeEventListener('scroll', handleScroll);
};
}
}, [openState, data, isThumbLoading]);
const GoFullScreen = (data: any) => {
setIsFullScreen(true);
setFullScreenData(data);
};
const [selectedSection, setSelectedSection] = useState<number | null>(null);
const handleSectionClick = (e: any, index: number) => {
// 이전에 선택한 요소를 가져옵니다.
const prevSelectedSection = document.querySelector(`.${selectedSectionClass}`);
if (prevSelectedSection) {
// 이전에 선택한 요소로부터 클래스를 제거합니다.
prevSelectedSection.classList.remove(selectedSectionClass);
}
// 현재 클릭한 요소에 클래스를 추가합니다.
const currentSection = e.currentTarget;
currentSection.classList.add(selectedSectionClass);
setSelectedSection(index);
};
const selectedSectionClass = 'selected-section'; // 선택된 요소에 적용할 CSS 클래스 이름
const scrollToSelectedSection = (index: number) => {
const scrollContainer = scrollContainerRef.current;
if (scrollContainer) {
const selectedSection = scrollContainer.querySelector(`section:nth-child(${index + 1})`) as HTMLElement;
if (selectedSection) {
// 선택한 섹션으로 스크롤 이동
scrollContainer.scrollTo({
left: selectedSection.offsetLeft - scrollContainer.offsetLeft,
behavior: 'smooth',
});
}
}
};
// 원본
const OriginImgComponent = ({ data }: any) => {
return (
<div className="flex">
{data?.map((item: any, index: number) => (
<section key={index}>
<div
className={`
rootNodeId:${item?.rootNodeId}, structureId:${item?.structureId}, depth:${item?.depth}, ancestorId:${item?.ancestorId}}
relative mr-[12px] h-[140px] w-[140px] cursor-pointer`}
>
<ReactTooltip
style={{ zIndex: 99 }}
className="reacttooltip"
id="my-tooltip-1"
place="right"
content={`rootNodeId:${item?.rootNodeId}, structureId:${item?.structureId}, depth:${item?.depth}, ancestorId:${item?.ancestorId}`}
/>
<img
data-tooltip-id="my-tooltip-1"
onClick={(e) => {
handleSectionClick(e, index);
scrollToSelectedSection(index); // 클릭 시 스크롤 이동 함수 호출
}}
className={`
${selectedSection === index ? selectedSectionClass : ''}
border-box reacttooltip block rounded-[6px]`}
src={item?.url}
alt={`rootNodeId:${item?.rootNodeId}, structureId:${item?.structureId}, depth:${item?.depth}, ancestorId:${item?.ancestorId}`}
/>
<div className="absolute top-0 left-[5px] text-[3px] font-light text-white">
<span className="text-white">{index + 1}</span>
</div>
<h3 className="absolute bottom-[5px] left-[50%] w-[100px] translate-x-[-50%] truncate text-center text-[10px] font-light text-white">
{item?.name}
</h3>
<div
onClick={async (e) => {
e.preventDefault();
setIsFullScreen(true);
await GoFullScreen(item?.url);
}}
className="absolute bottom-[5px] right-[5px] h-[14px] w-[14px] cursor-pointer bg-[url('/images/icons/Fullscreen.png')]"
/>
</div>
</section>
))}
</div>
);
};
// 익명화
const AnonyImgComponent = ({ data, selectedSection }: any) => {
return (
<div className="flex">
{data?.map((item: any, index: number) => (
<section key={index}>
<div
className={`
rootNodeId:${item?.rootNodeId}, structureId:${item?.structureId}, depth:${item?.depth}, ancestorId:${item?.ancestorId}}
relative mr-[12px] h-[140px] w-[140px] cursor-pointer`}
>
<ReactTooltip
style={{ zIndex: 99 }}
className="reacttooltip"
id="my-tooltip-2"
place="right"
content={`rootNodeId:${item?.rootNodeId}, structureId:${item?.structureId}, depth:${item?.depth}, ancestorId:${item?.ancestorId}`}
/>
<img
data-tooltip-id="my-tooltip-2"
onClick={(e) => {
handleSectionClick(e, index);
scrollToSelectedSection(index); // 클릭 시 스크롤 이동 함수 호출
}}
className={`
${selectedSection === index ? selectedSectionClass : ''}
block rounded-[6px]`}
src={item?.url}
alt={`rootNodeId:${item?.rootNodeId}, structureId:${item?.structureId}, depth:${item?.depth}, ancestorId:${item?.ancestorId}`}
/>
<div className="absolute top-0 left-[5px] text-[3px] font-light text-white">
<span className="text-white">{index + 1}</span>
</div>
<h3
className="className={`ml-[10px] ${ absolute bottom-0 left-[50%] block w-[100px] w-full translate-x-[-50%] cursor-pointer
truncate text-[10px] font-light text-white"
>
{item?.name}
</h3>
<div
onClick={async (e) => {
e.preventDefault();
setIsFullScreen(true);
await GoFullScreen(item.url);
}}
className="absolute bottom-[5px] right-[5px] h-[14px] w-[14px] cursor-pointer bg-[url('/images/icons/Fullscreen.png')]"
/>
</div>
</section>
))}
</div>
);
};
// function Length({ data }: any) {
// return <div className="text-[12px] font-light text-[#1FA1FF]">Total {data?.length}</div>;
// }
return (
<>
{openState ? (
<section
className={`absolute bottom-0 z-10 flex w-full items-center justify-center bg-black bg-opacity-70 py-6 transition-all duration-1000 ease-in-out`}
>
{isFullScreen && fullScreenData && (
<div className="fixed top-0 left-0 z-30 flex h-full w-full items-center justify-center">
<div className="absolute inset-0 bg-black opacity-90"></div>
<div className="relative flex h-[80%] w-[80%] items-center justify-center">
<div
onClick={() => setIsFullScreen(false)}
className="absolute top-4 right-4 h-8 w-8 cursor-pointer text-center text-white"
>
Close ✕
</div>
<img className="block h-full " src={fullScreenData} alt="DICOM" />
</div>
</div>
)}
<button
onClick={() => setOpenState((prev: boolean) => !prev)}
className="inner-shadow absolute top-[-20px] left-0 z-20 flex h-[20px] w-[85px] cursor-pointer justify-center rounded-t-[10px] bg-sub03 bg-no-repeat"
>
<div className={`${openState ? '' : 'scale-y-[-1] transform'}`}>
<DownChevronSvg color={'black'} className={'h-[20px] w-[20px]'} />
</div>
</button>
<article className={`${openState === true ? 'opacity-100' : 'opacity-0'} mr-[30px] w-[35%] `}>
<div className="flex w-full items-center justify-between">
<h2
className={`
mb-[5px] text-[12px] font-bold text-sub04`}
>
Original File
</h2>
{/*<Length data={data?.pages?.[0]?.data?.data?.originThumbList} />*/}
</div>
{isThumbLoading ? (
<div
className={`${
openState === true ? 'h-[170px]' : 'bottom-0 h-0'
} x-scrollbar-style flex w-full items-center justify-center overflow-y-clip overflow-x-scroll rounded-[10px] bg-base pl-[12px] pt-[15px]`}
>
<h1 className="text-[24px] font-bold text-stone-400">Loading...</h1>
{/*TODO:Loading처리하거나 file missing문구 띄우기*/}
{/*data?.pages[0].data.data.originThumbList*/}
</div>
) : (
<div
ref={(ref) => {
scrollContainerRef.current = ref; // 공유 ref 설정
originImgScrollContainerRef.current = ref; // 원본 이미지 컴포넌트의 ref 설정
}}
// ref={scrollContainerRef}
className={`${
openState === true ? 'h-[170px]' : 'bottom-0 h-0'
} x-scrollbar-style flex w-full items-center justify-start overflow-y-clip overflow-x-scroll rounded-[10px] bg-base pl-[12px] pt-[15px]`}
>
{data?.pages && (
<OriginImgComponent
data={data?.pages.flatMap((page) => page?.data?.data?.originThumbList)}
selectedSection={selectedSection}
/>
)}
</div>
)}
</article>
<article className={`${openState === true ? 'opacity-100' : 'opacity-0'} w-[50%]`}>
<div className="flex w-full items-center justify-between">
<h2
className={`${
openState === true ? 'opacity-100' : 'opacity-0'
} mb-[5px] text-[12px] font-bold text-sub04`}
>
Anonymized file
</h2>
{/*<Length data={data?.pages?.[0]?.data?.data?.anonyThumbList} />*/}
</div>
{data?.pages[0]?.data?.data?.anonyThumbList.length === 0 ? (
<div
className={` ${openState === true ? 'h-[170px] ' : 'bottom-0 h-0'}
font18Bol flex w-full items-center justify-center overflow-hidden rounded-[10px] bg-base`}
>
<div className="flex items-center justify-center text-black">
<div className="mr-[10px] h-[60px] w-[60px]">
<img className="block " src="/images/icons/none.png" alt="no anonymize img" />
</div>
Anonymized file missing
</div>
</div>
) : (
<div
ref={(ref) => {
anonyImgScrollContainerRef.current = ref; // 익명화 이미지 컴포넌트의 ref 설정
}}
//ref={scrollContainerRef}
className={`
${openState === true ? 'h-[170px] ' : 'bottom-0 h-0'}
x-scrollbar-style flex w-full items-center justify-start overflow-x-scroll rounded-[10px] bg-base pl-[12px] pt-[15px]`}
>
<AnonyImgComponent
data={data?.pages.flatMap((page) => page?.data?.data?.anonyThumbList)}
selectedSection={selectedSection}
/>
</div>
)}
</article>
</section>
) : (
<button
onClick={() => setOpenState((prev: boolean) => !prev)}
className="inner-shadow absolute top-[-20px] left-0 z-20 flex h-[20px] w-[85px] cursor-pointer justify-center rounded-t-[10px] bg-sub03 bg-no-repeat"
>
<div className={`${openState ? '' : 'scale-y-[-1] transform'}`}>
<DownChevronSvg color={'black'} className={'h-[20px] w-[20px]'} />
</div>
</button>
)}
</>
);
};
export default ImgSlide;