hejianhao
2026-05-25 5005eb2b39c2f6225f1599fd30b04dc2fae032b2
feat(robotaxi/home): 抽离通用轮询hook并优化车辆轮询逻辑
1个文件已添加
1个文件已修改
198 ■■■■■ 已修改文件
src/pages/robotaxi/home/hooks/use-limited-polling.ts 145 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/robotaxi/home/hooks/use-nearby-vehicles.ts 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/robotaxi/home/hooks/use-limited-polling.ts
New file
@@ -0,0 +1,145 @@
import { useDidShow } from '@tarojs/taro';
import { useCallback, useEffect, useRef } from 'react';
type UseLimitedPollingOptions = {
  intervalMs: number;
  maxPollCount: number;
  canPoll: () => boolean;
  onPoll: () => void;
  onPollInvalid?: () => void;
  onLimitReached?: () => void;
  shouldResumeOnShow?: () => boolean;
  onResumeBeforeStart?: () => void;
};
type UseLimitedPollingResult = {
  ensurePolling: () => void;
  restartPolling: () => void;
  stopPolling: () => void;
  disposePolling: () => void;
};
/**
 * 通用受限轮询控制:负责次数上限、启停、重置与页面回前台恢复。
 */
export default function useLimitedPolling({
  intervalMs,
  maxPollCount,
  canPoll,
  onPoll,
  onPollInvalid,
  onLimitReached,
  shouldResumeOnShow,
  onResumeBeforeStart
}: UseLimitedPollingOptions): UseLimitedPollingResult {
  const timerRef = useRef<ReturnType<typeof setInterval> | null>(null); // 当前轮询定时器
  const pollCountRef = useRef(0); // 当前轮询次数
  const stoppedByLimitRef = useRef(false); // 是否因超过轮询上限而停止
  const intervalMsRef = useRef(intervalMs); // 轮询间隔引用
  const maxPollCountRef = useRef(maxPollCount); // 最大轮询次数引用
  const canPollRef = useRef(canPoll); // 是否允许继续轮询
  const onPollRef = useRef(onPoll); // 单次轮询执行函数
  const onPollInvalidRef = useRef(onPollInvalid); // 轮询失效回调
  const onLimitReachedRef = useRef(onLimitReached); // 达到上限回调
  const shouldResumeOnShowRef = useRef(shouldResumeOnShow); // 页面展示时是否允许恢复
  const onResumeBeforeStartRef = useRef(onResumeBeforeStart); // 页面展示恢复前的预处理
  intervalMsRef.current = intervalMs;
  maxPollCountRef.current = maxPollCount;
  canPollRef.current = canPoll;
  onPollRef.current = onPoll;
  onPollInvalidRef.current = onPollInvalid;
  onLimitReachedRef.current = onLimitReached;
  shouldResumeOnShowRef.current = shouldResumeOnShow;
  onResumeBeforeStartRef.current = onResumeBeforeStart;
  /**
   * 清理轮询定时器。
   */
  const clearPollTimer = useCallback(() => {
    if (timerRef.current) {
      clearInterval(timerRef.current);
      timerRef.current = null;
    }
  }, []);
  /**
   * 停止轮询但保留当前计数状态。
   */
  const stopPolling = useCallback(() => {
    clearPollTimer();
  }, [clearPollTimer]);
  /**
   * 重置轮询状态,用于重新开始一轮完整计数。
   */
  const resetPollingState = useCallback(() => {
    pollCountRef.current = 0;
    stoppedByLimitRef.current = false;
  }, []);
  /**
   * 确保轮询已启动;已存在定时器时不重复创建。
   */
  const ensurePolling = useCallback(() => {
    if (timerRef.current || stoppedByLimitRef.current) {
      return;
    }
    timerRef.current = setInterval(() => {
      if (!canPollRef.current()) {
        stopPolling();
        onPollInvalidRef.current?.();
        return;
      }
      if (pollCountRef.current >= maxPollCountRef.current) {
        stopPolling();
        stoppedByLimitRef.current = true;
        onLimitReachedRef.current?.();
        return;
      }
      pollCountRef.current += 1;
      onPollRef.current();
    }, intervalMsRef.current);
  }, [stopPolling]);
  /**
   * 重新开始一轮轮询,并清空上一次累计次数。
   */
  const restartPolling = useCallback(() => {
    stopPolling();
    resetPollingState();
    ensurePolling();
  }, [ensurePolling, resetPollingState, stopPolling]);
  /**
   * 彻底销毁轮询状态,供页面离开或功能停用时调用。
   */
  const disposePolling = useCallback(() => {
    stopPolling();
    resetPollingState();
  }, [resetPollingState, stopPolling]);
  useDidShow(() => {
    if (!stoppedByLimitRef.current) {
      return;
    }
    if (!shouldResumeOnShowRef.current?.()) {
      return;
    }
    onResumeBeforeStartRef.current?.();
    restartPolling();
  });
  useEffect(() => {
    return () => {
      clearPollTimer();
    };
  }, [clearPollTimer]);
  return {
    ensurePolling,
    restartPolling,
    stopPolling,
    disposePolling
  };
}
src/pages/robotaxi/home/hooks/use-nearby-vehicles.ts
@@ -3,6 +3,7 @@
import { getNearbyVehicles } from '../../../../robotaxi/api/home';
import { getDistance } from '../../../../utils/Robotaxi-public-method';
import { HOME_MAP_ID } from '../constants';
import useLimitedPolling from './use-limited-polling';
type Deps = {
  isOperatingArea: boolean;
@@ -39,6 +40,7 @@
};
const POLL_INTERVAL_MS = 5000; // 车辆轮询周期(毫秒)
const MAX_POLL_COUNT = 30; // 最多轮询 5 分钟
const SETTLE_DELAY_MS = 500; // 地图停止后的静止判定时间(毫秒)
const MOVE_THRESHOLD_METERS = 500; // 触发新搜索的位移阈值(米)
const ANIMATION_DURATION_MS = 2000; // 车辆位移动画时长(毫秒)
@@ -154,7 +156,6 @@
 */
export default function useNearbyVehicles({ isOperatingArea }: Deps) {
  const [vehicleMarkerVersion, setVehicleMarkerVersion] = useState(0); // 车辆 marker 结构版本号,仅在结构变化时触发重渲染
  const pollTimerRef = useRef<any>(null); // 轮询定时器句柄
  const settleTimerRef = useRef<any>(null); // 停稳判定定时器句柄
  const animationSyncTimerRef = useRef<any>(null); // 动画结束后同步 React marker 的定时器
  const activeAnimationRef = useRef<VehicleAnimationState | null>(null); // 当前进行中的车辆动画状态
@@ -179,16 +180,6 @@
      mapContextRef.current = Taro.createMapContext(HOME_MAP_ID);
    }
    return mapContextRef.current;
  };
  /**
   * 清理轮询定时器。
   */
  const clearPollTimer = () => {
    if (pollTimerRef.current) {
      clearInterval(pollTimerRef.current);
      pollTimerRef.current = null;
    }
  };
  /**
@@ -275,7 +266,7 @@
   */
  const disposeVehiclesImpl = () => {
    clearSettleTimer();
    clearPollTimer();
    disposePolling();
    clearVehicles();
    activeSearchCenterRef.current = null;
    lastSearchCenterRef.current = null;
@@ -379,21 +370,29 @@
  fetchAndAnimateVehiclesRef.current = fetchAndAnimateVehiclesImpl;
  /**
   * 启动 5 秒轮询,轮询中心遵循 activeSearchCenterRef。
   * 读取当前搜索中心并执行一次车辆轮询。
   */
  const ensurePollingImpl = () => {
    if (pollTimerRef.current) return;
    pollTimerRef.current = setInterval(() => {
      const center = activeSearchCenterRef.current;
      if (!center || !isOperatingArea) {
        disposeVehiclesRef.current();
        return;
      }
      fetchAndAnimateVehiclesRef.current(center);
    }, POLL_INTERVAL_MS);
  };
  const pollVehicles = useCallback(() => {
    const center = activeSearchCenterRef.current;
    if (!center) {
      return;
    }
    fetchAndAnimateVehiclesRef.current(center);
  }, []);
  ensurePollingRef.current = ensurePollingImpl;
  const { ensurePolling, restartPolling, disposePolling } = useLimitedPolling({
    intervalMs: POLL_INTERVAL_MS,
    maxPollCount: MAX_POLL_COUNT,
    canPoll: () => Boolean(activeSearchCenterRef.current) && isOperatingArea,
    onPoll: pollVehicles,
    onPollInvalid: () => {
      disposeVehiclesRef.current();
    },
    shouldResumeOnShow: () => Boolean(activeSearchCenterRef.current) && isOperatingArea,
    onResumeBeforeStart: pollVehicles
  });
  ensurePollingRef.current = ensurePolling;
  /**
   * 激活指定中心点并立即拉起一次车辆查询。
@@ -401,8 +400,8 @@
  const activateCenterAndFetchImpl = (center: NearbyCenter) => {
    activeSearchCenterRef.current = center;
    lastSearchCenterRef.current = center;
    fetchAndAnimateVehiclesRef.current(center);
    ensurePollingRef.current();
    pollVehicles();
    restartPolling();
  };
  activateCenterAndFetchRef.current = activateCenterAndFetchImpl;