[SHOPTIME-3707] Home / Top / 우측 상단 / 포커스가 있는 상태에서 롤링이 됨

원인 : React depencency 오류로 인해 focus 상태를 제대로 체크하지 못함.
대책 : depencency 제거 하고 ref 로 변경
This commit is contained in:
yonghyon
2024-10-15 17:46:53 +09:00
parent cc68482e89
commit e8ba1bbf4f

View File

@@ -1,54 +1,31 @@
import React, { import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from 'react';
import classNames from 'classnames'; import classNames from "classnames";
import { import { useDispatch, useSelector } from "react-redux";
useDispatch,
useSelector,
} from 'react-redux';
import SpotlightContainerDecorator import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator";
from '@enact/spotlight/SpotlightContainerDecorator'; import Spottable from "@enact/spotlight/Spottable";
import Spottable from '@enact/spotlight/Spottable';
import btnPlay from '../../../../assets/images/btn/btn-play-thumb-nor.png'; import btnPlay from "../../../../assets/images/btn/btn-play-thumb-nor.png";
import defaultLogoImg import defaultLogoImg from "../../../../assets/images/ic-tab-partners-default@3x.png";
from '../../../../assets/images/ic-tab-partners-default@3x.png'; import emptyHorImage from "../../../../assets/images/img-home-banner-empty-hor.png";
import emptyHorImage import emptyVerImage from "../../../../assets/images/img-home-banner-empty-ver.png";
from '../../../../assets/images/img-home-banner-empty-hor.png'; import defaultImageItem from "../../../../assets/images/img-thumb-empty-product@3x.png";
import emptyVerImage import liveShow from "../../../../assets/images/tag-liveshow.png";
from '../../../../assets/images/img-home-banner-empty-ver.png'; import { setBannerIndex } from "../../../actions/homeActions";
import defaultImageItem import { sendLogTopContents } from "../../../actions/logActions";
from '../../../../assets/images/img-thumb-empty-product@3x.png'; import { pushPanel } from "../../../actions/panelActions";
import liveShow from '../../../../assets/images/tag-liveshow.png'; import { startVideoPlayer } from "../../../actions/playActions";
import { setBannerIndex } from '../../../actions/homeActions'; import CustomImage from "../../../components/CustomImage/CustomImage";
import { sendLogTopContents } from '../../../actions/logActions'; import usePriceInfo from "../../../hooks/usePriceInfo";
import { pushPanel } from '../../../actions/panelActions'; import { LOG_MENU, LOG_TP_NO, panel_names } from "../../../utils/Config";
import { startVideoPlayer } from '../../../actions/playActions'; import { $L, formatGMTString } from "../../../utils/helperMethods";
import CustomImage from '../../../components/CustomImage/CustomImage';
import usePriceInfo from '../../../hooks/usePriceInfo'; import css from "./RollingUnit.module.less";
import {
LOG_MENU,
LOG_TP_NO,
panel_names,
} from '../../../utils/Config';
import {
$L,
formatGMTString,
} from '../../../utils/helperMethods';
import css from './RollingUnit.module.less';
const SpottableComponent = Spottable("div"); const SpottableComponent = Spottable("div");
const Container = SpotlightContainerDecorator( const Container = SpotlightContainerDecorator({ enterTo: "last-focused" }, "div");
{ enterTo: "last-focused" },
"div"
);
const LINK_TYPES = { const LINK_TYPES = {
FEATURED_BRANDS: "DSP00501", FEATURED_BRANDS: "DSP00501",
@@ -75,12 +52,7 @@ const createPanelInfo = (data, categoryData = {}) => ({
focusedContainerId: null, focusedContainerId: null,
}); });
export default function RollingUnit({ export default function RollingUnit({ bannerData, spotlightId, isHorizontal, handleItemFocus }) {
bannerData,
spotlightId,
isHorizontal,
handleItemFocus,
}) {
const rollingData = bannerData.bannerDetailInfos; const rollingData = bannerData.bannerDetailInfos;
const rollingDataLength = bannerData.bannerDetailInfos.length; const rollingDataLength = bannerData.bannerDetailInfos.length;
@@ -88,25 +60,20 @@ export default function RollingUnit({
const curationId = useSelector((state) => state.home?.bannerData?.curationId); const curationId = useSelector((state) => state.home?.bannerData?.curationId);
const curtNm = useSelector((state) => state.home?.bannerData?.curtNm); const curtNm = useSelector((state) => state.home?.bannerData?.curtNm);
const shptmTmplCd = useSelector( const shptmTmplCd = useSelector((state) => state.home?.bannerData?.shptmTmplCd);
(state) => state.home?.bannerData?.shptmTmplCd
);
const nowMenu = useSelector((state) => state.common.menu.nowMenu); const nowMenu = useSelector((state) => state.common.menu.nowMenu);
const entryMenu = useSelector((state) => state.common.menu.entryMenu); const entryMenu = useSelector((state) => state.common.menu.entryMenu);
const homeCategory = useSelector( const homeCategory = useSelector((state) => state.home.menuData?.data?.homeCategory);
(state) => state.home.menuData?.data?.homeCategory
);
const countryCode = useSelector((state) => state.common.httpHeader.cntry_cd); const countryCode = useSelector((state) => state.common.httpHeader.cntry_cd);
const bannerId = `banner-${bannerData.banrLctnNo}`; const bannerId = `banner-${bannerData.banrLctnNo}`;
const savedIndex = useSelector((state) => state.home.bannerIndices[bannerId]); const savedIndex = useSelector((state) => state.home.bannerIndices[bannerId]);
const [startIndex, setStartIndex] = useState( const [startIndex, setStartIndex] = useState(savedIndex !== undefined ? savedIndex : 0);
savedIndex !== undefined ? savedIndex : 0 const lastIndexRef = useRef(rollingDataLength - 1);
); const doRollingRef = useRef(false);
const lastIndex = rollingDataLength - 1; const [unitHasFocus, setUnitHasFocus] = useState(false);
const [rollingFocus, setRollingFocus] = useState(false);
const [contentsFocus, setContentsFocus] = useState(false); const [contentsFocus, setContentsFocus] = useState(false);
const [prevFocus, setPrevFocus] = useState(false); const [prevFocus, setPrevFocus] = useState(false);
const [nextFocus, setNextFocus] = useState(false); const [nextFocus, setNextFocus] = useState(false);
@@ -153,11 +120,7 @@ export default function RollingUnit({
return { return {
banrNo: `${currentRollingData?.banrDpOrd}`, banrNo: `${currentRollingData?.banrDpOrd}`,
banrTpNm: currentRollingData?.vtctpYn banrTpNm: currentRollingData?.vtctpYn ? (currentRollingData.vtctpYn === "Y" ? "Vertical" : "Horizontal") : "",
? currentRollingData.vtctpYn === "Y"
? "Vertical"
: "Horizontal"
: "",
contId, contId,
contNm, contNm,
contTpNm: currentRollingData?.shptmBanrTpNm ?? "", contTpNm: currentRollingData?.shptmBanrTpNm ?? "",
@@ -179,37 +142,27 @@ export default function RollingUnit({
} }
}, [savedIndex]); }, [savedIndex]);
const animate = useCallback( const animate = useCallback((time) => {
(time) => { if (previousTimeRef.current != undefined) {
if (previousTimeRef.current != undefined) { const deltaTime = time - previousTimeRef.current;
const deltaTime = time - previousTimeRef.current;
if (deltaTime >= 10000 && !rollingFocus && rollingDataLength > 1) { if (deltaTime >= 10000 && doRollingRef.current) {
setStartIndex((prevIndex) => setStartIndex((prevIndex) => (prevIndex === lastIndexRef.current ? 0 : prevIndex + 1));
prevIndex === lastIndex ? 0 : prevIndex + 1
);
previousTimeRef.current = time;
}
} else {
previousTimeRef.current = time; previousTimeRef.current = time;
} }
} else {
previousTimeRef.current = time;
}
if (typeof window === "object") { if (typeof window === "object") {
requestRef.current = window.requestAnimationFrame(animate); requestRef.current = window.requestAnimationFrame(animate);
} }
},
[rollingFocus, rollingDataLength, lastIndex]
);
// 롤링 시작 여부 true = stop, false = start
const rollingStart = useCallback((focus) => {
setRollingFocus(focus);
}, []); }, []);
// 롤링 이전 버튼 // 롤링 이전 버튼
const handlePrev = useCallback(() => { const handlePrev = useCallback(() => {
if (startIndex === 0) { if (startIndex === 0) {
setStartIndex(lastIndex); setStartIndex(lastIndexRef.current);
return; return;
} }
setStartIndex(startIndex - 1); setStartIndex(startIndex - 1);
@@ -217,27 +170,25 @@ export default function RollingUnit({
// 롤링 다음 버튼 // 롤링 다음 버튼
const handleNext = useCallback(() => { const handleNext = useCallback(() => {
if (lastIndex === startIndex) { if (lastIndexRef.current === startIndex) {
setStartIndex(0); setStartIndex(0);
return; return;
} }
setStartIndex(startIndex + 1); setStartIndex(startIndex + 1);
}, [startIndex, lastIndex]); }, [startIndex]);
// 베너 포커스 인 // 베너 포커스 인
const onFocus = useCallback(() => { const onFocus = useCallback(() => {
if (handleItemFocus) { if (handleItemFocus) {
handleItemFocus(); handleItemFocus();
} }
rollingStart(true); setUnitHasFocus(true);
setRollingFocus(true); }, [handleItemFocus]);
}, [handleItemFocus, rollingStart]);
// 배너 포커스 아웃 // 배너 포커스 아웃
const onBlur = () => { const onBlur = useCallback(() => {
rollingStart(false); setUnitHasFocus(false);
setRollingFocus(false); }, []);
};
// 인디케이터 아래키 누를시 [<] // 인디케이터 아래키 누를시 [<]
const prevKeyDown = (event) => { const prevKeyDown = (event) => {
@@ -264,17 +215,17 @@ export default function RollingUnit({
setNextFocus(false); setNextFocus(false);
setContentsFocus(false); setContentsFocus(false);
setPrevFocus(false); setPrevFocus(false);
rollingStart(true); setUnitHasFocus(true);
}, [handleItemFocus, rollingStart]); }, [handleItemFocus]);
// 인디케이터 포커스 아웃 // 인디케이터 포커스 아웃
const indicatorBlur = () => { const indicatorBlur = useCallback(() => {
rollingStart(false); setUnitHasFocus(false);
setTimeout(() => { setTimeout(() => {
setNextFocus(false); setNextFocus(false);
setContentsFocus(false); setContentsFocus(false);
}, 300); }, 300);
}; }, []);
const categoryData = useMemo(() => { const categoryData = useMemo(() => {
if ( if (
@@ -282,9 +233,7 @@ export default function RollingUnit({
rollingData[startIndex].shptmLnkTpCd === LINK_TYPES.CATEGORY rollingData[startIndex].shptmLnkTpCd === LINK_TYPES.CATEGORY
) { ) {
if (homeCategory && homeCategory.length > 0) { if (homeCategory && homeCategory.length > 0) {
const foundCategory = homeCategory.find( const foundCategory = homeCategory.find((data) => data.lgCatCd === rollingData[startIndex].lgCatCd);
(data) => data.lgCatCd === rollingData[startIndex].lgCatCd
);
if (foundCategory) { if (foundCategory) {
return { return {
lgCatNm: foundCategory.lgCatNm, lgCatNm: foundCategory.lgCatNm,
@@ -341,17 +290,11 @@ export default function RollingUnit({
break; break;
case LINK_TYPES.TRENDING_NOW: case LINK_TYPES.TRENDING_NOW:
handlePushPanel( handlePushPanel(panel_names.TRENDING_NOW_PANEL, createPanelInfo(currentData));
panel_names.TRENDING_NOW_PANEL,
createPanelInfo(currentData)
);
break; break;
case LINK_TYPES.HOT_PICKS: case LINK_TYPES.HOT_PICKS:
handlePushPanel( handlePushPanel(panel_names.HOT_PICKS_PANEL, createPanelInfo(currentData));
panel_names.HOT_PICKS_PANEL,
createPanelInfo(currentData)
);
break; break;
case LINK_TYPES.ON_SALE: case LINK_TYPES.ON_SALE:
@@ -362,10 +305,7 @@ export default function RollingUnit({
case LINK_TYPES.CATEGORY: case LINK_TYPES.CATEGORY:
if (Object.keys(categoryData).length > 0) { if (Object.keys(categoryData).length > 0) {
handlePushPanel( handlePushPanel(panel_names.CATEGORY_PANEL, createPanelInfo(currentData, categoryData));
panel_names.CATEGORY_PANEL,
createPanelInfo(currentData, categoryData)
);
} }
break; break;
@@ -397,15 +337,7 @@ export default function RollingUnit({
logTpNo: LOG_TP_NO.TOP_CONTENTS.CLICK, logTpNo: LOG_TP_NO.TOP_CONTENTS.CLICK,
}) })
); );
}, [ }, [rollingData, startIndex, bannerId, dispatch, categoryData, handlePushPanel, topContentsLogInfo]);
rollingData,
startIndex,
bannerId,
dispatch,
categoryData,
handlePushPanel,
topContentsLogInfo,
]);
const videoClick = useCallback(() => { const videoClick = useCallback(() => {
if (bannerId) { if (bannerId) {
@@ -433,32 +365,26 @@ export default function RollingUnit({
logTpNo: LOG_TP_NO.TOP_CONTENTS.CLICK, logTpNo: LOG_TP_NO.TOP_CONTENTS.CLICK,
}) })
); );
}, [ }, [rollingData, startIndex, bannerId, dispatch, handleStartVideoPlayer, topContentsLogInfo]);
rollingData,
startIndex,
bannerId,
dispatch,
handleStartVideoPlayer,
topContentsLogInfo,
]);
// 10초 롤링 // 10초 롤링
useEffect(() => { useEffect(() => {
// 데이터가 1개인 경우 및 포커스 있는 경우 롤링 하지 않음 // 데이터가 1개인 경우 및 포커스 있는 경우 롤링 하지 않음
if (rollingDataLength === 1 || rollingFocus) { lastIndexRef.current = rollingDataLength - 1;
return; previousTimeRef.current = undefined;
} if (rollingDataLength <= 1 || unitHasFocus) {
doRollingRef.current = false;
if (typeof window === "object") {
requestRef.current = window.requestAnimationFrame(animate);
}
return () => {
if (typeof window === "object") { if (typeof window === "object") {
window.cancelAnimationFrame(requestRef.current); window.cancelAnimationFrame(requestRef.current);
} }
}; return;
}, [rollingDataLength, rollingFocus, animate]); }
doRollingRef.current = true;
if (typeof window === "object") {
requestRef.current = window.requestAnimationFrame(animate);
}
}, [rollingDataLength, unitHasFocus]);
useEffect(() => { useEffect(() => {
let _nowMenu = nowMenu; let _nowMenu = nowMenu;
@@ -489,12 +415,7 @@ export default function RollingUnit({
}, [nowMenu]); }, [nowMenu]);
return ( return (
<Container <Container className={classNames(css.rollingWrap, isHorizontal && css.isHorizontalWrap)}>
className={classNames(
css.rollingWrap,
isHorizontal && css.isHorizontalWrap
)}
>
{rollingDataLength !== 1 ? ( {rollingDataLength !== 1 ? (
<SpottableComponent <SpottableComponent
className={classNames(css.arrow, css.leftBtn)} className={classNames(css.arrow, css.leftBtn)}
@@ -508,8 +429,7 @@ export default function RollingUnit({
/> />
) : null} ) : null}
{rollingData && {rollingData && rollingData[startIndex].shptmBanrTpNm === "Image Banner" ? (
rollingData[startIndex].shptmBanrTpNm === "Image Banner" ? (
<SpottableComponent <SpottableComponent
className={classNames(css.itemBox, isHorizontal && css.isHorizontal)} className={classNames(css.itemBox, isHorizontal && css.isHorizontal)}
onClick={imageBannerClick} onClick={imageBannerClick}
@@ -518,9 +438,7 @@ export default function RollingUnit({
spotlightId={spotlightId} spotlightId={spotlightId}
spotlightDisabled={contentsFocus} spotlightDisabled={contentsFocus}
aria-label={ aria-label={
rollingData[startIndex].prdtNm rollingData[startIndex].prdtNm ? rollingData[startIndex].prdtNm : rollingData[startIndex].tmnlImgNm
? rollingData[startIndex].prdtNm
: rollingData[startIndex].tmnlImgNm
} }
> >
<div className={css.imgBanner}> <div className={css.imgBanner}>
@@ -538,28 +456,14 @@ export default function RollingUnit({
aria-label={"LIVE " + rollingData[startIndex].showNm} aria-label={"LIVE " + rollingData[startIndex].showNm}
> >
<p className={css.liveIcon}> <p className={css.liveIcon}>
<CustomImage <CustomImage delay={0} src={liveShow} animationSpeed="fast" ariaLabel="LIVE icon" />
delay={0}
src={liveShow}
animationSpeed="fast"
ariaLabel="LIVE icon"
/>
</p> </p>
<div <div className={classNames(css.itemBox, isHorizontal && css.isHorizontal)}>
className={classNames(
css.itemBox,
isHorizontal && css.isHorizontal
)}
>
{rollingData[startIndex].tmnlImgPath == null ? ( {rollingData[startIndex].tmnlImgPath == null ? (
<CustomImage <CustomImage
delay={0} delay={0}
src={ src={rollingData[startIndex].vtctpYn === "Y" ? emptyVerImage : emptyHorImage}
rollingData[startIndex].vtctpYn === "Y"
? emptyVerImage
: emptyHorImage
}
ariaLabel={rollingData[startIndex].tmnlImgNm} ariaLabel={rollingData[startIndex].tmnlImgNm}
fallbackSrc={isHorizontal ? emptyHorImage : emptyVerImage} fallbackSrc={isHorizontal ? emptyHorImage : emptyVerImage}
/> />
@@ -576,11 +480,7 @@ export default function RollingUnit({
{rollingData[startIndex].tmnlImgPath == null ? ( {rollingData[startIndex].tmnlImgPath == null ? (
"" ""
) : ( ) : (
<CustomImage <CustomImage delay={0} src={btnPlay} ariaLabel="Play video Button" />
delay={0}
src={btnPlay}
ariaLabel="Play video Button"
/>
)} )}
</div> </div>
</div> </div>
@@ -605,20 +505,11 @@ export default function RollingUnit({
spotlightDisabled={contentsFocus} spotlightDisabled={contentsFocus}
aria-label={rollingData[startIndex].showNm} aria-label={rollingData[startIndex].showNm}
> >
<div <div className={classNames(css.itemBox, isHorizontal && css.isHorizontal)}>
className={classNames(
css.itemBox,
isHorizontal && css.isHorizontal
)}
>
{rollingData[startIndex].tmnlImgPath == null ? ( {rollingData[startIndex].tmnlImgPath == null ? (
<CustomImage <CustomImage
delay={0} delay={0}
src={ src={rollingData[startIndex].vtctpYn === "Y" ? emptyVerImage : emptyHorImage}
rollingData[startIndex].vtctpYn === "Y"
? emptyVerImage
: emptyHorImage
}
ariaLabel={rollingData[startIndex].tmnlImgNm} ariaLabel={rollingData[startIndex].tmnlImgNm}
/> />
) : ( ) : (
@@ -633,11 +524,7 @@ export default function RollingUnit({
{rollingData[startIndex].tmnlImgPath == null ? ( {rollingData[startIndex].tmnlImgPath == null ? (
"" ""
) : ( ) : (
<CustomImage <CustomImage delay={0} src={btnPlay} ariaLabel="Play video Button" />
delay={0}
src={btnPlay}
ariaLabel="Play video Button"
/>
)} )}
</div> </div>
@@ -669,9 +556,7 @@ export default function RollingUnit({
spotlightId={spotlightId} spotlightId={spotlightId}
spotlightDisabled={contentsFocus} spotlightDisabled={contentsFocus}
aria-label={ aria-label={
rollingData[startIndex].prdtNm rollingData[startIndex].prdtNm ? rollingData[startIndex].prdtNm : rollingData[startIndex].tmnlImgNm
? rollingData[startIndex].prdtNm
: rollingData[startIndex].tmnlImgNm
} }
> >
<div className={css.productInfo}> <div className={css.productInfo}>
@@ -688,13 +573,9 @@ export default function RollingUnit({
: discountRate : discountRate
? discountedPrice ? discountedPrice
: originalPrice} : originalPrice}
{discountRate && !isHorizontal && ( {discountRate && !isHorizontal && <span className={css.saleAccBox}>{originalPrice}</span>}
<span className={css.saleAccBox}>{originalPrice}</span>
)}
</div> </div>
{isHorizontal && ( {isHorizontal && <span className={css.saleAccBox}>{originalPrice}</span>}
<span className={css.saleAccBox}>{originalPrice}</span>
)}
</div> </div>
<div className={css.itemImgBox}> <div className={css.itemImgBox}>