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

React Native(Expo)数组场景下Expo Video的useRef使用及重渲染重置问题

解决方案

问题根因

  • 单ref覆盖:你当前使用单个全局video ref,循环渲染多个Video组件时,后渲染的视频实例会覆盖前一个存到ref里的实例,因此只有最后一个视频可以正常控制播放。
  • 子组件重渲染重置:之前封装子组件出现视频重置,是因为父组件状态更新触发重渲染时,子组件没有做重渲染拦截,导致子组件被卸载后重新挂载,视频恢复到初始状态。另外你原有代码中视频格式判断逻辑存在语法错误,所有媒体都会被识别为视频格式。

实现方案

1. 封装独立视频子组件,用React.memo避免不必要重渲染

子组件内部自己维护ref、播放状态,只要传入的视频地址等核心props不变,父组件其他状态更新不会触发子组件重渲染,也就不会重置视频进度。

import React, { memo, useRef, useState } from 'react';
import { TouchableOpacity } from 'react-native';
import Video from 'expo-video'; // 替换为你实际引入的Video组件路径

const VideoItem = memo(({ videoUrl }) => {
  const videoRef = useRef(null);
  const [isPlaying, setIsPlaying] = useState(false);

  const togglePlay = async () => {
    if (isPlaying) {
      await videoRef.current.pauseAsync();
    } else {
      await videoRef.current.playAsync();
    }
  };

  return (
    <TouchableOpacity onPress={togglePlay} style={{ flex: 1 }}>
      <Video
        ref={videoRef}
        style={{ alignSelf: 'center' }}
        source={{ uri: videoUrl }}
        onPlaybackStatusUpdate={(status) => {
          setIsPlaying(status.isPlaying);
        }}
      />
    </TouchableOpacity>
  );
});

export default VideoItem;

2. 修正父组件逻辑,补充稳定key、修正格式判断

import React, { useState } from 'react';
import {
  SafeAreaView,
  View,
  FlatList,
  StyleSheet,
  Text,
  StatusBar,
  Image,
} from 'react-native';
import VideoItem from './VideoItem';

function App(props) {
  const [allData, setAllData] = useState([
    {
      medias: [
        { link: 'https://link.com/link1.avi', mediaExtension: 'avi' },
        { link: 'https://link.com/link2.jpg', mediaExtension: 'jpg' },
        { link: 'https://link.com/link3.mov', mediaExtension: 'mov' },
      ],
      name: 'Name',
      description: 'description',
    },
  ]);
  // 定义支持的视频格式数组
  const VIDEO_EXTENSIONS = ['mov', 'avi', 'WebM'];

  return (
    <View style={{}}>
      <FlatList
        horizontal
        data={allData}
        keyExtractor={(item, index) => `group-${index}`}
        renderItem={({ item }) => (
          <View style={{}}>
            {item.medias.map((media, index) => (
              // 必须给循环项加稳定的唯一key,避免React误删组件导致重置
              <View key={`media-${media.link || index}`}>
                {VIDEO_EXTENSIONS.includes(media.mediaExtension) ? (
                  <VideoItem videoUrl={media.link} />
                ) : (
                  <Image style={{}} source={{ uri: media.link }} />
                )}
              </View>
            ))}
          </View>
        )}
      />
    </View>
  );
}

export default App;

关键优化点说明

  • React.memo会对传入子组件的props做浅比较,只要视频地址没有变化,子组件就不会触发重渲染,自然不会重置视频播放进度。
  • 所有循环渲染的项都必须加稳定唯一的key,避免React在diff时错误复用/卸载组件,导致视频被重置。
  • 修正了原有视频格式判断的逻辑错误,不会再把图片识别为视频渲染。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 20:42:02