【问题标题】:Having some troubles getting useEffect, useCallback and setTimeout to work together让 useEffect、useCallback 和 setTimeout 一起工作时遇到一些麻烦
【发布时间】:2023-04-03 14:30:02
【问题描述】:

尝试根据宽度为滑块实现一些自定义滚动行为。它使用 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 的目的是,当您通过更改当前日期来更改容器内的内容时,它没有注册一堆超时。

【问题讨论】:

    标签: javascript reactjs settimeout use-effect usecallback


    【解决方案1】:

    你应该为你的 useEffect 添加一个清理函数。由于您在 useEffect 中对 isTimeoutLocked 进行了变异,因此它可能会强制多个 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 将 NOT BATCH 它们,因此您的 handleScroll 中的 EVERY setState 将导致重新渲染(如果它不在 setTimeout/async 函数中React 会批量处理它们)。由于您有很多相互关联的状态,因此您应该研究 useReducer。

    【讨论】:

      猜你喜欢
      • 2018-01-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-09-19
      • 1970-01-01
      相关资源
      最近更新 更多