refactor(mp): 长轮询通用化 — useLongPolling hook + 咨询详情页接入
- 新增 useLongPolling hook:generation counter 防重叠、useDidShow/Hide 可见性控制、失败退避、enabled 守卫
- 患者端 + 医生端 consultation/detail 接入,删除约 80 行重复长轮询代码
- 架构建议 5/5 全部完成 ✅
This commit is contained in:
93
apps/miniprogram/src/hooks/useLongPolling.ts
Normal file
93
apps/miniprogram/src/hooks/useLongPolling.ts
Normal file
@@ -0,0 +1,93 @@
|
||||
import { useRef, useEffect } from 'react';
|
||||
import { useDidShow, useDidHide } from '@tarojs/taro';
|
||||
|
||||
interface LongPollOptions<T> {
|
||||
/** 轮询函数,返回新数据或 null/undefined */
|
||||
pollFn: () => Promise<T | null | undefined>;
|
||||
/** 收到新数据后的回调 */
|
||||
onData: (data: T) => void;
|
||||
/** 是否启用轮询(受外部状态控制) */
|
||||
enabled: boolean;
|
||||
/** 成功轮询间隔 ms,默认 3000 */
|
||||
intervalMs?: number;
|
||||
/** 连续失败上限,默认 50 */
|
||||
maxFailures?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用长轮询 hook。
|
||||
*
|
||||
* - generation counter 杜绝新旧循环重叠
|
||||
* - useDidShow/useDidHide 控制页面可见性
|
||||
* - 失败退避:delay = min(failCount * 2000, 30000)
|
||||
* - 自动在 enabled 变 false 时停止(如会话关闭)
|
||||
*/
|
||||
export function useLongPolling<T>({
|
||||
pollFn,
|
||||
onData,
|
||||
enabled,
|
||||
intervalMs = 3000,
|
||||
maxFailures = 50,
|
||||
}: LongPollOptions<T>) {
|
||||
const generation = useRef(0);
|
||||
const mountedRef = useRef(true);
|
||||
const pollFnRef = useRef(pollFn);
|
||||
const onDataRef = useRef(onData);
|
||||
pollFnRef.current = pollFn;
|
||||
onDataRef.current = onData;
|
||||
|
||||
const start = () => {
|
||||
const gen = ++generation.current;
|
||||
runPoll(gen, 0);
|
||||
};
|
||||
|
||||
const stop = () => {
|
||||
generation.current += 1;
|
||||
};
|
||||
|
||||
const runPoll = async (gen: number, failCount: number) => {
|
||||
if (gen !== generation.current || !mountedRef.current) return;
|
||||
if (failCount >= maxFailures) return;
|
||||
try {
|
||||
const data = await pollFnRef.current();
|
||||
if (gen !== generation.current || !mountedRef.current) return;
|
||||
if (data != null) {
|
||||
onDataRef.current(data);
|
||||
}
|
||||
failCount = 0;
|
||||
} catch {
|
||||
failCount++;
|
||||
}
|
||||
if (gen !== generation.current || !mountedRef.current) return;
|
||||
const delay = failCount > 0 ? Math.min(failCount * 2000, 30000) : intervalMs;
|
||||
await new Promise<void>((r) => setTimeout(r, delay));
|
||||
if (gen === generation.current && mountedRef.current) {
|
||||
runPoll(gen, failCount);
|
||||
}
|
||||
};
|
||||
|
||||
// enabled 变 false → 停止;变 true → 启动
|
||||
useEffect(() => {
|
||||
if (enabled) {
|
||||
start();
|
||||
} else {
|
||||
stop();
|
||||
}
|
||||
}, [enabled]);
|
||||
|
||||
useDidShow(() => {
|
||||
if (enabled) start();
|
||||
});
|
||||
useDidHide(() => {
|
||||
stop();
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
generation.current += 1;
|
||||
mountedRef.current = false;
|
||||
};
|
||||
}, []);
|
||||
|
||||
return { start, stop };
|
||||
}
|
||||
Reference in New Issue
Block a user