【问题标题】:Strange scroll behaviour when using useState in React在 React 中使用 useState 时出现奇怪的滚动行为
【发布时间】:2022-01-11 09:02:42
【问题描述】:

我开始在 react 中实现一个轮播,此外,我想给它一个无限滚动的效果,也就是说,如果你一直向右点击并到达终点。从头开始。

有趣的是,我不得不使用 useState 并且我注意到非常奇怪的行为。它永远不会到达最后一个 div,而是立即转到第一个(无滚动效果)并滚动到第二个。

这是滚动发生的主文件。

Carousel.tsx

import React, { FC, useEffect, useLayoutEffect, useRef, useState } from "react";
import { useStyles, useStylesClasses } from "./CarouselCSS";
import ArrowRightAltIcon from '@mui/icons-material/ArrowRightAlt';

interface Props {
    className?: string;
}

type Scroll = "left" | "right";

type Index = {
    sign: "To" | "By";
    value: number;
}

export const Carousel: FC<Props> = ({className, children}) => {
    const {Root, Carousel} = useStyles();
    const classes = useStylesClasses();

    const ref = useRef(null);
    const firstRun = useRef(true);

    const [activeIndex, setActiveIndex] = useState<Index>({
        sign: "To",
        value: 0
    });

    // Triggers before rendering the page
    // useLayoutEffect(()=>{
    //     console.log(ref.current.children.length);
    // }, [ref])

    useEffect(() => {
        if(!firstRun.current)
        if (activeIndex.sign === "To"){
            console.log({"To": activeIndex.value})
             ref.current.scrollTo({
                 left: (activeIndex.value*ref.current.clientWidth),
                 behavior: "smooth",
             })
        }
        else{
            console.log("By")
            ref.current.scrollBy({
                left: ref.current.clientWidth,
                behavior: "smooth",
            })
        }
        else
        firstRun.current = false;        
    }, [activeIndex])

    // controlling scrolling and making it infinite
    const scroll = (value: Scroll) => {
        switch(value){
            case "left": {
                console.log("Left")
                if(activeIndex.value === 0){
                    setActiveIndex({sign: "By", value: ref.current.children.length - 1});
                }
                else{
                    setActiveIndex({sign: "To", value: activeIndex.value - 1});
                }
            }
            break;
            case "right": {
                console.log("Right")
                if(activeIndex.value === ref.current.children.length - 1){
                    setActiveIndex({sign: "To", value: 0});
                }
                else{
                    setActiveIndex({sign: "By", value: activeIndex.value + 1});
                }
            }
            break;
        }
    }

    return (
        <Root>
            <div onClick={() => {
                        scroll("left");
                    }
                }
            >
                <ArrowRightAltIcon style={{transform: 'rotate(180deg)'}} className={classes.arrow}/>
            </div>
            <Carousel className={className} ref={ref}>
                    {children}
            </Carousel>
            <div onClick={() => {
                    scroll("right");
                }}
            >
                <ArrowRightAltIcon className={classes.arrow}/>
            </div>
        </Root>
    )
}

如果这不合理,我还将代码沙箱链接附加到整个内容。

更新 正如所建议的,组件重新渲染,因此为什么第一个 div 会立即出现。现在,我想在这里理解的是,为什么它在某些情况下会重新渲染并在某些情况下滚动?既然我用的是useState,不应该每次都重新渲染吗?

https://codesandbox.io/embed/keen-wright-9n94s?fontsize=14&hidenavigation=1&theme=dark

感谢任何帮助!

【问题讨论】:

    标签: javascript reactjs


    【解决方案1】:

    那是因为当你推动 div 移动时,点击事件触发重新渲染组件。这意味着它不会滚动,它只会向您显示具有更新的 activeIndex 的新组件。这就是它的行为不流畅的原因。所以你需要找到其他正确的滚动方式。为了避免在点击最后一个时移动到第一个,将最后一个索引检查功能添加到滚动功能中。如果它是最后一个索引,则什么都不返回,但这不是一个好的解决方案。我建议你去寻找并学习如何在 div 框中使用滚动 api 移动。学完就知道解决办法了。

    【讨论】:

    • 我在想它可能会重新渲染,但它让我感到困惑,因为它也在正常滚动。所以,我猜,它是在特定用例中重新渲染。哪个本身很奇怪?知道为什么会这样吗?
    • React 在使用 useState 更新状态时重新渲染组件。在您的代码中,您在单击 div 时调用了 setActiveIndex。这意味着每当您单击时组件都会重新呈现。这导致了重新渲染,这就是您想在幕后了解的内容 :) 如果我的回答对您有帮助,请选择我的回答 Thx:D
    • 是的,我明白了。但我不明白的是,为什么它不在某些时候重新渲染?如果您看到沙盒并尝试滚动,您会看到它也正常滚动!这就是让我困惑的地方!总之,谢谢你的回答!!
    猜你喜欢
    • 1970-01-01
    • 2021-04-14
    • 2022-08-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-30
    相关资源
    最近更新 更多