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

React自定义视频播放Hook使用useRef在iOS Safari返回时报TypeError

问题原因

  1. 页面点击返回触发组件卸载时,React会先把绑定的ref置为null,而iOS Safari的IntersectionObserver在页面销毁的过程中仍可能触发回调,此时访问videoRef.current就会为空导致报错。
  2. 现有代码缺少对ref存在性的防御判断,且IntersectionObserver的清理逻辑不够彻底。
  3. 你的useEffect依赖数组写法存在小问题:wrapperRef本身是稳定的ref对象,不需要作为依赖,而你调用Hook时传入的options是字面量对象,每次组件渲染都会生成新实例,会导致useEffect重复执行。

修复步骤

1. 调整自定义Hook逻辑

  • 给回调函数增加ref存在性判断
  • 清理函数中追加observer.disconnect()彻底销毁监听
  • 修正useEffect依赖项
  • 给play方法加catch捕获潜在异常(iOS Safari对视频播放的限制较多,避免未捕获错误)

修复后的usePlayVideo代码:

const usePlayVideo = options => {
  const wrapperRef = useRef(null);
  const videoRef = useRef(null);

  const handleVid = entries => {
    const [entry] = entries;
    // 先判断ref是否存在,为空直接终止逻辑
    if (!videoRef.current) return;
    if (entry.isIntersecting) {
      // play返回Promise,加catch避免播放限制导致的未捕获错误
      videoRef.current.play().catch(() => {});
    } else {
      videoRef.current.pause();
    }
  };

  useEffect(() => {
    const wrapper = wrapperRef.current;
    if (!wrapper) return;
    const observer = new IntersectionObserver(handleVid, options);
    observer.observe(wrapper);
    return () => {
      observer.unobserve(wrapper);
      // 彻底销毁观察者,避免组件卸载后触发回调
      observer.disconnect();
    };
    // ref对象本身稳定不需要作为依赖,仅保留options即可
  }, [options]);

  return [wrapperRef, videoRef];
};
export { usePlayVideo };

2. 优化调用侧的options(可选但推荐)

因为你调用时传入的options是字面量,每次渲染都会生成新对象,导致useEffect重复执行,建议用useMemo包一下:

function ClientAsk() {
  // 缓存options,避免重复触发useEffect
  const observerOptions = useMemo(() => ({
    root: null,
    rootMargin: "0px",
    threshold: 0.3,
  }), []);
  const [wrapperRef, videoRef] = usePlayVideo(observerOptions);

  return (
    {/* bunch of other divs and stuff */}
    <div className="grid_s_start_3 grid_s_span_20" ref={wrapperRef}>
      <video ref={videoRef} id="video" playsInline muted preload="auto" loop style={{ width: "100%" }}>
        <source src={ClientVid} type="video/mp4" />
      </video>
    </div>
    {/* rest of other divs and stuff */}
    );
}

export default ClientAsk;

补充说明

这个问题确实是iOS Safari独有的事件触发时机差异导致的,不是useRef默认值设为null的问题,加防御判断和彻底清理观察者即可解决。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 12:15:02