Files
JKVideo/hooks/useLiveDetail.ts
Developer e3def7d01b fix: 修复小窗闭包过期、详情页 loading 卡死、视频播放器性能问题
- 小窗 PanResponder 用 storeRef 替代闭包捕获,修复 roomId/bvid 始终为初始值
- useLiveDetail 用 ref 比对替代 cancelled 标志,防止 fetch 被意外取消
- 详情页 useLayoutEffect 同步清除小窗,BigVideoCard 小窗活跃时跳过播放
- 视频播放器竖屏/全屏互斥渲染,减半解码器占用
- onProgress 节流 + 退出全屏强制恢复播放
2026-03-25 15:03:49 +08:00

69 lines
2.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { useState, useEffect, useCallback, useRef } from 'react';
import { getLiveRoomDetail, getLiveAnchorInfo, getLiveStreamUrl } from '../services/bilibili';
import type { LiveRoomDetail, LiveAnchorInfo, LiveStreamInfo } from '../services/types';
interface LiveDetailState {
room: LiveRoomDetail | null;
anchor: LiveAnchorInfo | null;
stream: LiveStreamInfo | null;
loading: boolean;
error: string | null;
}
export function useLiveDetail(roomId: number) {
const [state, setState] = useState<LiveDetailState>({
room: null,
anchor: null,
stream: null,
loading: true,
error: null,
});
// 用 ref 追踪最新的 roomId避免 cancelled 闭包问题
const latestRoomId = useRef(roomId);
latestRoomId.current = roomId;
useEffect(() => {
if (!roomId) return;
setState({ room: null, anchor: null, stream: null, loading: true, error: null });
const fetchId = roomId; // 捕获当前 roomId
async function doFetch() {
try {
const [room, anchor] = await Promise.all([
getLiveRoomDetail(fetchId),
getLiveAnchorInfo(fetchId),
]);
// 仅在 roomId 未变化时更新状态(替代 cancelled 模式)
if (latestRoomId.current !== fetchId) return;
let stream: LiveStreamInfo = { hlsUrl: '', flvUrl: '', qn: 0, qualities: [] };
if (room?.live_status === 1) {
stream = await getLiveStreamUrl(fetchId);
}
if (latestRoomId.current !== fetchId) return;
setState({ room, anchor, stream, loading: false, error: null });
} catch (e: any) {
if (latestRoomId.current !== fetchId) return;
setState(prev => ({ ...prev, loading: false, error: e?.message ?? '加载失败' }));
}
}
doFetch();
}, [roomId]);
const changeQuality = useCallback(async (qn: number) => {
try {
const stream = await getLiveStreamUrl(roomId, qn);
setState(prev => ({ ...prev, stream: { ...stream, qn } }));
} catch { /* ignore */ }
}, [roomId]);
return { ...state, changeQuality };
}