fix(frontend): frontend UI keep flashing (#10352)

This commit is contained in:
Hiep Le 2025-08-15 15:19:30 +07:00 committed by GitHub
parent f8376a9702
commit a1ffe5c936
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 42 additions and 17 deletions

View File

@ -1,5 +1,5 @@
import clsx from "clsx";
import React, { useEffect, useRef, useState } from "react";
import React, { useRef, useState } from "react";
import { NavTab } from "./nav-tab";
import { ScrollLeftButton } from "./scroll-left-button";
import { ScrollRightButton } from "./scroll-right-button";
@ -25,18 +25,12 @@ export function Container({
children,
className,
}: ContainerProps) {
const [containerWidth, setContainerWidth] = useState(0);
const [canScrollLeft, setCanScrollLeft] = useState(false);
const [canScrollRight, setCanScrollRight] = useState(false);
const [showScrollButtons, setShowScrollButtons] = useState(false);
const containerRef = useRef<HTMLDivElement | null>(null);
const scrollContainerRef = useRef<HTMLDivElement>(null);
// Track container width using ResizeObserver
useTrackElementWidth({
elementRef: containerRef,
callback: setContainerWidth,
});
// Check scroll position and update button states
const updateScrollButtons = () => {
if (scrollContainerRef.current) {
@ -47,10 +41,19 @@ export function Container({
}
};
// Update scroll buttons when tabs change or container width changes
useEffect(() => {
updateScrollButtons();
}, [labels, containerWidth]);
// Track container width using ResizeObserver
useTrackElementWidth({
elementRef: containerRef,
callback: (width: number) => {
// Only update scroll button visibility when crossing the threshold
const shouldShowScrollButtons =
width < 598 && Boolean(labels) && labels!.length > 0;
if (shouldShowScrollButtons) {
setShowScrollButtons(shouldShowScrollButtons);
}
updateScrollButtons();
},
});
// Scroll functions
const scrollLeft = () => {
@ -65,8 +68,6 @@ export function Container({
}
};
const showScrollButtons = containerWidth < 598 && labels && labels.length > 0;
return (
<div
ref={containerRef}

View File

@ -1,18 +1,38 @@
import { useEffect } from "react";
import { useEffect, useCallback, useRef } from "react";
interface UseTrackElementWidthProps {
elementRef: React.RefObject<HTMLElement | null>;
callback: (width: number) => void;
delay?: number; // Optional delay parameter with default
}
export const useTrackElementWidth = ({
elementRef,
callback,
delay = 100, // Default 100ms delay
}: UseTrackElementWidthProps) => {
const timeoutRef = useRef<NodeJS.Timeout | null>(null);
// Create debounced callback that only fires after delay
const debouncedCallback = useCallback(
(width: number) => {
// Clear existing timeout
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
// Set new timeout
timeoutRef.current = setTimeout(() => {
callback(width);
}, delay);
},
[callback, delay],
);
useEffect(() => {
const resizeObserver = new ResizeObserver((entries) => {
for (const entry of entries) {
callback(entry.contentRect.width);
debouncedCallback(entry.contentRect.width);
}
});
@ -21,7 +41,11 @@ export const useTrackElementWidth = ({
}
return () => {
// Clean up timeout and observer
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
resizeObserver.disconnect();
};
}, []);
}, [debouncedCallback]);
};