diff --git a/week08/mission/Chap8_mission/src/hooks/useThrottle.ts b/week08/mission/Chap8_mission/src/hooks/useThrottle.ts index d9c89bc..d5e9fee 100644 --- a/week08/mission/Chap8_mission/src/hooks/useThrottle.ts +++ b/week08/mission/Chap8_mission/src/hooks/useThrottle.ts @@ -1,63 +1,42 @@ import { useEffect, useRef, useState } from "react"; -// value가 true로 바뀔 때 지정한 delay 시간 동안 한 번만 true로 반영 -// value가 false로 바뀌면 즉시 false로 반영 -// value: boolean 상태 값 (ex: inView) -// delay: throttle 간격(ms) -// throttledValue: throttle 적용된 상태 값 - -function useThrottle(value: boolean, delay: number) { - // throttledValue: 실제로 외부에 반환되는 상태 +function useThrottle(value: T, delay: number): T { const [throttledValue, setThrottledValue] = useState(value); - - // lastExecuted: 마지막으로 throttledValue를 true로 업데이트한 시간 - const lastExecuted = useRef(0); - - // timer: delay 시간 후에 업데이트하기 위한 setTimeout ID - const timer = useRef(null); // 브라우저 환경에서 setTimeout은 number 반환 + const lastExecuted = useRef(Date.now()); + const timer = useRef(null); useEffect(() => { - // value가 false이면 즉시 반영 - if (!value) { - setThrottledValue(false); + const now = Date.now(); + const remaining = delay - (now - lastExecuted.current); - // 이미 등록된 타이머가 있다면 취소 - if (timer.current) { - clearTimeout(timer.current); - timer.current = null; - } - return; // 이후 로직 실행 X + // 기존 타이머 제거 + if (timer.current) { + clearTimeout(timer.current); + timer.current = null; } - const now = Date.now(); // 현재 시간 - const remaining = delay - (now - lastExecuted.current); - // lastExecuted 기준으로 남은 delay 계산 - if (remaining <= 0) { - // 마지막 실행 이후 delay 이상 지났으면 즉시 true 반영 + // delay 지나면 즉시 실행 lastExecuted.current = now; - setThrottledValue(true); + setThrottledValue(value); } else { - // delay가 남아 있으면 이전 타이머 취소 후 새 타이머 등록 - if (timer.current) clearTimeout(timer.current); - + // 남은 시간만큼 타이머 timer.current = window.setTimeout(() => { - lastExecuted.current = Date.now(); // 실행 시간 업데이트 - setThrottledValue(true); // throttledValue 갱신 - timer.current = null; // 타이머 리셋 + lastExecuted.current = Date.now(); + setThrottledValue(value); + timer.current = null; }, remaining); } - // Clean-up: effect 재실행 전 타이머 정리 return () => { if (timer.current) { clearTimeout(timer.current); timer.current = null; } }; - }, [value, delay]); // value 또는 delay 변경 시 effect 실행 + }, [value, delay]); - return throttledValue; // throttle 적용된 상태 반환 + return throttledValue; } export default useThrottle; \ No newline at end of file diff --git a/week08/mission/Chap8_mission/src/pages/HomePage.tsx b/week08/mission/Chap8_mission/src/pages/HomePage.tsx index 71babca..f2fe586 100644 --- a/week08/mission/Chap8_mission/src/pages/HomePage.tsx +++ b/week08/mission/Chap8_mission/src/pages/HomePage.tsx @@ -25,14 +25,22 @@ const HomePage = () => { console.log("data:", data); const { ref, inView } = useInView({ threshold: 0 }); - const throttledInView = useThrottle(inView, 3000); - // throttledInView 감시 + const shouldFetch = inView && !isFetching && hasNextPage; + const throttledInView = useThrottle(shouldFetch, 1000); + useEffect(() => { - if (throttledInView && !isFetching && hasNextPage) { + if (throttledInView) { fetchNextPage(); } - }, [throttledInView, isFetching, hasNextPage, fetchNextPage]); + }, [throttledInView, fetchNextPage]); + + // throttledInView 감시 + // useEffect(() => { + // if (throttledInView && !isFetching && hasNextPage) { + // fetchNextPage(); + // } + // }, [throttledInView, isFetching, hasNextPage, fetchNextPage]); if (isError) { return
Error occurred while fetching data.
; diff --git a/week09/mission/Chap9_mission/src/App.tsx b/week09/mission/Chap9_mission/src/App.tsx index d0d3ebc..da92af1 100644 --- a/week09/mission/Chap9_mission/src/App.tsx +++ b/week09/mission/Chap9_mission/src/App.tsx @@ -1,15 +1,13 @@ import "./App.css"; -import { Provider } from "react-redux"; import { CartList } from "./components/CartList"; import { Navbar } from "./components/Navbar"; -import store from "./store/store"; function App() { return ( - +
- +
); } diff --git a/week09/mission/Chap9_mission/src/components/CartItem.tsx b/week09/mission/Chap9_mission/src/components/CartItem.tsx index ed66aa1..56cf42b 100644 --- a/week09/mission/Chap9_mission/src/components/CartItem.tsx +++ b/week09/mission/Chap9_mission/src/components/CartItem.tsx @@ -1,22 +1,21 @@ import type { Lp } from "../types/cart"; -import { useDispatch } from "../hooks/useCustomRedux"; -import { decrement, increment } from "../slices/cartSlice"; +import { useCartActions } from "../hooks/useCartStore"; interface CartItemProps { lp: Lp; } export const CartItem = ({ lp }: CartItemProps) => { - const dispatch = useDispatch(); + const { increase, decrease } = useCartActions(); const handleIncrease = () => { // 수량 증가 액션 디스패치 - dispatch(increment({ id: lp.id })); + increase(lp.id); }; const handleDecrease = () => { // 수량 감소 액션 디스패치 - dispatch(decrement({ id: lp.id })); + decrease(lp.id); }; return ( diff --git a/week09/mission/Chap9_mission/src/components/CartList.tsx b/week09/mission/Chap9_mission/src/components/CartList.tsx index 78a3e7b..ba8430f 100644 --- a/week09/mission/Chap9_mission/src/components/CartList.tsx +++ b/week09/mission/Chap9_mission/src/components/CartList.tsx @@ -1,10 +1,11 @@ import { CartItem } from "./CartItem"; import { PriceBox } from "./PriceBox"; import { ClearBtn } from "./ClearBtn"; -import { useSelector } from "../hooks/useCustomRedux"; +import { useCartInfo } from "../hooks/useCartStore"; + export const CartList = () => { - const { cartItems } = useSelector((state) => state.cart); + const { cartItems } = useCartInfo(); return (
{cartItems.map((item) => ( diff --git a/week09/mission/Chap9_mission/src/components/ClearBtn.tsx b/week09/mission/Chap9_mission/src/components/ClearBtn.tsx index 9be65ab..1ba47d8 100644 --- a/week09/mission/Chap9_mission/src/components/ClearBtn.tsx +++ b/week09/mission/Chap9_mission/src/components/ClearBtn.tsx @@ -1,20 +1,27 @@ -import { useDispatch } from "../hooks/useCustomRedux"; -import { clearCart } from "../slices/cartSlice"; +import DeleteModal from "./DeleteModal"; +import { useModalActions } from "../hooks/useModalStore"; +import { useModalInfo } from "../hooks/useModalStore"; export const ClearBtn = () => { - const dispatch = useDispatch(); + const { isOpen } = useModalInfo(); + const { openModal } = useModalActions(); - const handleClearCart = () => { - // 수량 증가 액션 디스패치 - dispatch(clearCart()); + const handleModalOpen = () => { + openModal(); }; return ( - + <> + + {/* 모달 표시 */} + {isOpen && ( + + )} + ); }; diff --git a/week09/mission/Chap9_mission/src/components/DeleteModal.tsx b/week09/mission/Chap9_mission/src/components/DeleteModal.tsx new file mode 100644 index 0000000..bf064fa --- /dev/null +++ b/week09/mission/Chap9_mission/src/components/DeleteModal.tsx @@ -0,0 +1,98 @@ +import { useCartActions } from "../hooks/useCartStore"; +import { useModalActions } from "../hooks/useModalStore"; + +const DeleteModal = () => { + const { clearCart } = useCartActions(); + const { closeModal } = useModalActions(); + + const handleClearCart = () => { + // 수량 증가 액션 디스패치 + clearCart(); + closeModal(); + }; + + const handleModalClose = () => { + closeModal(); + }; + + return ( +
+ {/* 오버레이 */} +