1

尝试根据宽度为滑块实现一些自定义滚动行为。它使用 useState 来跟踪滑块内的当前页面和总页面。这是我最终尝试编译它的代码,但它有一些意想不到的行为。

const [isTimeoutLocked, setIsTimeoutLocked] = useState(false)
const handleScroll = useCallback(() => {
  
    const gridContainer = document.querySelector(".grid");
    const totalPages = Math.ceil(
            gridContainer.scrollWidth / gridContainer.clientWidth + -0.1
        );
    setTotalPages(totalPages);
    const scrollPos = gridContainer.clientWidth + gridContainer.scrollLeft + 2;
    if (gridContainer.scrollWidth > scrollPos) {
        gridContainer.scrollBy({
            left: gridContainer.clientWidth + 20.5,
            behavior: "auto",
            block: "center",
            inline: "center",
        });
        setCurrentPage(currentPage + 1);
        setIsTimeoutLocked(false)
        } else {
            gridContainer.scrollTo(0, 0);
            setCurrentPage(1);
            setIsTimeoutLocked(false)
        }
    },[currentPage]);


    useEffect(() => {
    if(!isTimeoutLocked){
        setTimeout(() => {
            setIsTimeoutLocked(true)
            document.querySelector(".grid") && handleScroll();
        }, 5000);
    }
    
}, [currentPage, displayDate, isTimeoutLocked, handleScroll]);

这里的问题是,当 currentPage 显示在 ui 中时,如果 else 在 handleScroll 函数中运行,它会重置回 1,这完全没问题,但它会导致返回到以前的值,而不是返回到 2 时到下一页。此外,由于我已将 isTimeoutLocked 添加为 useEffect 的依赖项,因为它要求它 setTimeout 将更频繁地运行,但我只想将它作为依赖项来获得正确的值,而不是这样 useEffect 每次运行时都会运行它改变。isTimeoutLocked 的目的是,当您通过更改当前日期来更改容器内的内容时,它没有注册一堆超时。

4

1 回答 1

0

您应该为您的 useEffect 添加一个清理功能。由于您isTimeoutLocked在 useEffect 中发生了变异,它可以强制多个 setTimeouts 运行,这可能是奇怪行为的结果。

在 useEffect 中使用 setTimeout 时,始终建议将其与清理一起使用。清理将在下一次 useEffect 触发之前运行,这使您有机会摆脱下一次 setTimeout 触发。它的代码是这样的:


useEffect(() => {
    if(!isTimeoutLocked){
        const tid = setTimeout(() => {
            setIsTimeoutLocked(true)
            document.querySelector(".grid") && handleScroll();
        }, 5000);

       // this is the cleanup function that is run before next useEffect
       return () => clearTimeout(tid);
    }

您可以在此处找到更多信息:https ://reactjs.org/docs/hooks-effect.html#effects-with-cleanup 。

要注意的另一件事是 setState 和批处理的异步性质。由于您在 setTimeout 中使用 setState,因此 React不会对它们进行批处理,因此您的handleScroll中的每个setState 都会导致重新渲染(如果它不在 setTimeout/async 函数中,React 会对其进行批处理)。由于您有很多相互关联的状态,因此您应该研究 useReducer。

于 2021-01-08T13:00:21.453 回答