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

Redux State无法通过dispatch更新:移动端Roadmap滑动跳转异常

移动端Roadmap滑动切换阶段跳过中间项的修复方案

问题描述

开发反馈网站的Roadmap页面时,移动端实现了Planned、In-Progress、Live三个阶段的左右滑动切换功能,使用触摸滑动检测代码后出现异常:滑动到Live或Planned阶段后,尝试返回In-Progress阶段时会直接跳过该阶段,跳转到另一端的阶段。

复现步骤

  • 使用开发者工具切换到移动端模式
  • 点击菜单图标
  • 点击Roadmap区块的view链接进入Roadmap路由
  • 通过触摸滑动(可用鼠标模拟)切换到Live阶段
  • 尝试滑动返回In-Progress阶段

问题根源

  1. 状态获取方式错误:事件监听中直接调用store.getState()获取currentStage,未使用React-Redux的useSelector,导致组件无法响应Redux状态的更新,始终拿不到最新的阶段值。
  2. Effect依赖缺失:绑定滑动事件的useEffect依赖项为空数组,事件回调函数会一直捕获初始的currentStage值,计算目标阶段时基于旧值判断,导致跳过中间阶段。

修复方案

1. 使用useSelector获取最新状态

替换直接调用store.getState()的方式,通过useSelector订阅Redux状态,确保组件能实时获取最新的currentStage值。

2. 为Effect添加正确依赖并优化回调

用useCallback包裹滑动处理函数,将currentStage和dispatch作为依赖项,确保状态更新时回调函数能拿到最新值;同时将包裹后的回调作为useEffect的依赖,保证事件监听能更新到最新的回调逻辑。

3. 限制滑动仅切换相邻阶段

在滑动处理逻辑中,基于当前阶段的索引计算目标阶段,限制每次滑动只能切换到前一个或后一个阶段,避免跨阶段跳转。

代码示例

修改后的RoadmapStages组件

import { useCallback, useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { setCurrentStage } from '../features/roadmap/roadmapSlice';

const RoadmapStages = () => {
  const dispatch = useDispatch();
  // 使用useSelector获取最新的currentStage
  const currentStage = useSelector(state => state.roadmap.currentStage);
  const stageOrder = ['Planned', 'In-Progress', 'Live'];

  // 用useCallback包裹滑动处理函数,依赖currentStage和dispatch
  const handleStageSwitch = useCallback((direction) => {
    const currentIndex = stageOrder.indexOf(currentStage);
    let newIndex;

    if (direction === 'left') {
      // 左滑:切换到下一个阶段,不能超过最后一个索引
      newIndex = Math.min(stageOrder.length - 1, currentIndex + 1);
    } else if (direction === 'right') {
      // 右滑:切换到上一个阶段,不能小于0
      newIndex = Math.max(0, currentIndex - 1);
    }

    dispatch(setCurrentStage(stageOrder[newIndex]));
  }, [currentStage, dispatch]);

  useEffect(() => {
    let touchStartX = 0;
    let touchEndX = 0;

    const handleTouchStart = (e) => {
      touchStartX = e.changedTouches[0].screenX;
    };

    const handleTouchEnd = (e) => {
      touchEndX = e.changedTouches[0].screenX;
      const swipeThreshold = 50; // 滑动距离阈值,避免误触
      const diff = touchStartX - touchEndX;

      if (Math.abs(diff) > swipeThreshold) {
        handleStageSwitch(diff > 0 ? 'left' : 'right');
      }
    };

    window.addEventListener('touchstart', handleTouchStart);
    window.addEventListener('touchend', handleTouchEnd);

    return () => {
      window.removeEventListener('touchstart', handleTouchStart);
      window.removeEventListener('touchend', handleTouchEnd);
    };
  }, [handleStageSwitch]); // 依赖handleStageSwitch,确保回调更新

  // 渲染各阶段内容
  return (
    <div className="roadmap-stages">
      {/* 根据currentStage渲染对应阶段的内容 */}
      {stageOrder.map(stage => (
        <div 
          key={stage} 
          className={`stage ${currentStage === stage ? 'active' : ''}`}
        >
          <h2>{stage}</h2>
          {/* 阶段内容 */}
        </div>
      ))}
    </div>
  );
};

export default RoadmapStages;

Roadmap Redux Slice定义

import { createSlice } from '@reduxjs/toolkit';

const initialState = {
  currentStage: 'In-Progress' // 默认显示中间阶段
};

const roadmapSlice = createSlice({
  name: 'roadmap',
  initialState,
  reducers: {
    setCurrentStage: (state, action) => {
      state.currentStage = action.payload;
    }
  }
});

export const { setCurrentStage } = roadmapSlice.actions;
export default roadmapSlice.reducer;

说明

修改后,组件会实时响应Redux状态的变化,滑动时只会切换到相邻的阶段,解决了跳过In-Progress的问题。同时添加了滑动距离阈值,能有效避免误触导致的不必要切换。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 00:55:03