You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React滚轮导航:可滚动页面不触发,无滚动时跳转下一页

解决React布局中滚轮触发页面跳转的判断问题

你遇到的核心问题是wheel事件触发时机早于scroll事件,导致没法通过scrolling变量判断这次滚轮操作是否真的会让页面滚动。不用依赖scroll事件,直接在wheel事件里计算容器的滚动状态就行。

修改后的代码

function Layout({error}: Props) {
    const {pathname} = useLocation();
    const {data: user, isLoading} = useUser();
    const navigate = useNavigate();
    const parent = useRef<HTMLDivElement>(null);

    useEffect(() => {
        const parentDiv = parent.current;
        if (!parentDiv) return;

        const handleWheel = (e: WheelEvent) => {
            const { scrollTop, scrollHeight, clientHeight } = parentDiv;
            // 判断是否滚到顶部且向上滚动
            const isAtTop = scrollTop === 0 && e.deltaY < 0;
            // 判断是否滚到底部且向下滚动
            const isAtBottom = scrollTop + clientHeight >= scrollHeight - 1 && e.deltaY > 0;

            if (isAtTop || isAtBottom) {
                // 阻止默认滚动行为(因为已经滚不动了)
                e.preventDefault();
                // 这里可以根据需要调整跳转逻辑,比如按路由顺序跳转
                const nextPath = getNextPath(pathname);
                if (nextPath) {
                    navigate(nextPath);
                }
            }
        };

        parentDiv.addEventListener("wheel", handleWheel, { passive: false });
        return () => {
            parentDiv.removeEventListener("wheel", handleWheel);
        };
    }, [pathname, navigate])

    // 自定义路由跳转顺序,替换成你的实际路由列表
    const getNextPath = (currentPath: string) => {
        const routes = ["/dashboard", "/page1", "/page2", "/page3"];
        const currentIndex = routes.findIndex(path => path === currentPath);
        if (currentIndex === -1 || currentIndex === routes.length - 1) {
            return null; // 可以改成返回第一个路由实现循环跳转
        }
        return routes[currentIndex + 1];
    }

    console.log("loading layout");
    const loadView = () => {
        if (error) return <ErrorPage />;
        if (pathname === "/dashboard") {
            if(isLoading) return <Box display="flex" justifyContent="center" width="100%" height="100%" alignItems="center"><CircularProgress /></Box>
            if(user?.username) return <DashboardPage />
            return <ErrorPage />
        }
        return <Outlet />;
    }

    return (
        <ThemeProvider theme={darkTheme}>
            <CssBaseline />
            <div ref={parent} id="wheeler" style={{width:"100vw", height: "100vh", overflowY: "auto"}}>
                <NavBar />
                <Stack direction="row" sx={{width: "100vw", height: PAGE_HEIGHT}}>
                <Box sx={{display: {sm: "flex", xs: "none"}}}>
                    <SocialMedia />
                </Box>
                {loadView()}
                </Stack>
            </div>
        </ThemeProvider>
    );
}

export default Layout;

关键说明

  1. 直接判断滚动边界:在wheel事件里直接获取容器的scrollTop、scrollHeight和clientHeight,结合滚轮方向(deltaY正负)判断是否已经滚到顶部/底部,无需等待滞后的scroll事件。
  2. 阻止默认行为:当确定容器无法继续滚动时,调用e.preventDefault()避免页面出现无效滚动的抖动。
  3. 自定义路由逻辑:通过getNextPath函数控制页面跳转顺序,你可以根据实际路由列表调整,比如支持循环跳转、指定特定页面的下一页等。
  4. 清理事件监听:在useEffect的返回函数中移除事件监听,避免组件卸载后出现内存泄漏。

内容的提问来源于stack exchange,提问作者Chrishdev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 22:05:16