onViewableItemsChanged 即使在导航到反应本机平面列表中的不同屏幕后也会被调用

onViewableItemsChanged is being called even after navigating to different screen in react native flatlist

我遇到了来自平面列表的 onViewableItemsChanged 道具的问题。当屏幕上显示单个视频时,我有一个正在播放的视频列表,我做到了。

但问题是,一旦视频在滚动时自动开始播放,我就会导航到另一个有文本输入字段的地方。一旦为文本输入打开键盘,就会调用另一个屏幕中的 onViewableItemsChanged,视频会继续在文本输入屏幕中播放。

下面是两个不同屏幕的代码:

FlatListSample.js

import React, {useState, useCallback} from 'react';
import {StyleSheet, Text, View, FlatList, TouchableOpacity} from 'react-native';
import YouTubeIFrame from 'react-native-youtube-iframe';

const data = [
  {
    id: '60524193a4e2070001537f51',
    title: 'React Native Tutorial #1 - Introduction',
    videoLink: 'ur6I5m2nTvk',
  },
  {
    id: '6051f9bba4e2070001537f50',
    title: 'React Native Tutorial #2 - Creating a React Native App',
    videoLink: 'pflXnUNMsNk',
  },
  {
    id: '6051f98accf9d60001f80429',
    title: 'React Native Tutorial #3 - Views, Text & Styles',
    videoLink: '_YydVvnjNFE',
  },
  {
    id: '6051f94accf9d60001f80428',
    title: 'React Native Tutorial #4 - Using State',
    videoLink: '1FiIYaRr148',
  },
  {
    id: '6051f921ccf9d60001f80427',
    title: 'React Native Tutorial #5 - Text Inputs',
    videoLink: 'c9Sg9jDitm8',
  },
  {
    id: '6051f8e8ccf9d60001f80426',
    title: 'React Native Tutorial #6 - Lists & ScrollView',
    videoLink: 'W-pg1r6-T0g',
  },
  {
    id: '6051f897a4e2070001537f4f',
    title: 'React Native Tutorial #7 - Flat List Component',
    videoLink: 'iMCM1NceGJY',
  },
  {
    id: '6051f84ca4e2070001537f4e',
    title: 'React Native Tutorial #8 - Touchable Components',
    videoLink: 'QhX25YGf8qg',
  },
  {
    id: '6051f817ccf9d60001f80425',
    title: 'React Native Tutorial #9 - Todo App (part 1)',
    videoLink: 'uLHFPt9B2Os',
  },
  {
    id: '6051f7dba4e2070001537f4d',
    title: 'React Native Tutorial #10 - Todo App (part 2)',
    videoLink: 'SGEitne8N-Q',
  },
];

const FlatListSample = props => {
  const [visibleItemIndex, setVisibleItemIndex] = useState();
  const [viewabilityConfiguration, setViewabilityConfiguration] = useState({
    waitForInteraction: true,
    viewAreaCoveragePercentThreshold: 40,
  });

  const onViewableItemsChangedHandler = useCallback(
    ({viewableItems, changed}) => {
      console.log('Viewable item');
      if (viewableItems && viewableItems.length !== 0) {
        setVisibleItemIndex(viewableItems[0].index);
      }
    },
    [],
  );

  const renderItem = ({item, index}) => {
    return (
      <View key={item.id} style={styles.videoSec}>
        <Text style={styles.videoTitle}>{item.title}</Text>
        <TouchableOpacity
          activeOpacity={0.9}
          onPress={() => {
            setVisibleItemIndex(null);
            props.navigation.navigate('TextInputSample');
          }}>
          <YouTubeIFrame
            videoId={item.videoLink}
            height={230}
            play={index === visibleItemIndex}
            initialPlayerParams={{rel: false, controls: false, loop: true}}
          />
        </TouchableOpacity>
      </View>
    );
  };
  return (
    <View style={styles.container}>
      <FlatList
        style={{flex: 1}}
        data={data}
        renderItem={renderItem}
        keyExtractor={item => item.id}
        viewabilityConfig={viewabilityConfiguration}
        onViewableItemsChanged={onViewableItemsChangedHandler}
      />
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#F7F7F7',
  },
  videoSec: {
    flex: 1,
    paddingVertical: 10,
    borderBottomColor: '#FCEABC',
    borderBottomWidth: 3,
    backgroundColor: '#FFFFFF',
  },
  videoTitle: {
    fontSize: 16,
    color: '#1B110A',
    paddingHorizontal: 10,
    marginBottom: 5,
  },
});

export default FlatListSample;

TextInputSample.js

import React from 'react';
import {View, TextInput} from 'react-native';

const TextInputSample = () => {
  return (
    <View>
      <TextInput
        style={{height: 40, backgroundColor: 'azure', fontSize: 20}}
        placeholder="Type here to translate!"
        onChangeText={text => console.log(text)}
      />
    </View>
  );
};

export default TextInputSample;

当我从 FlatListSample.js 滚动到 TextInputSample.js 并打开键盘时,视频开始播放再次。有人可以找到解决方案吗!

提前感谢您的解决方案。

好吧,我刚刚在 IOS 和 Android 中都进行了验证。看来问题仅在 android 中。这也是一种奇怪的行为。

试图通过查看react navigation screens堆栈来查找问题的原因,调试了很多但还无法跟踪。

对于你的情况,我想出了两种方法来解决你的问题。 (打开以获得更好的解决方案)

  1. change focused boolean value with navigation eventListeners 'blur' and 'focus'
import React, {useState, useRef, useEffect} from 'react';
import { useCallback } from 'react';
import {StyleSheet, Text, View, FlatList, TouchableOpacity} from 'react-native';
import YouTubeIFrame from 'react-native-youtube-iframe'; 

const data = [
  {
    id: '60524193a4e2070001537f51',
    title: 'React Native Tutorial #1 - Introduction',
    videoLink: 'ur6I5m2nTvk',
  },
  {
    id: '6051f9bba4e2070001537f50',
    title: 'React Native Tutorial #2 - Creating a React Native App',
    videoLink: 'pflXnUNMsNk',
  },
  {
    id: '6051f98accf9d60001f80429',
    title: 'React Native Tutorial #3 - Views, Text & Styles',
    videoLink: '_YydVvnjNFE',
  },
  {
    id: '6051f94accf9d60001f80428',
    title: 'React Native Tutorial #4 - Using State',
    videoLink: '1FiIYaRr148',
  },
  {
    id: '6051f921ccf9d60001f80427',
    title: 'React Native Tutorial #5 - Text Inputs',
    videoLink: 'c9Sg9jDitm8',
  },
  {
    id: '6051f8e8ccf9d60001f80426',
    title: 'React Native Tutorial #6 - Lists & ScrollView',
    videoLink: 'W-pg1r6-T0g',
  },
  {
    id: '6051f897a4e2070001537f4f',
    title: 'React Native Tutorial #7 - Flat List Component',
    videoLink: 'iMCM1NceGJY',
  },
  {
    id: '6051f84ca4e2070001537f4e',
    title: 'React Native Tutorial #8 - Touchable Components',
    videoLink: 'QhX25YGf8qg',
  },
  {
    id: '6051f817ccf9d60001f80425',
    title: 'React Native Tutorial #9 - Todo App (part 1)',
    videoLink: 'uLHFPt9B2Os',
  },
  {
    id: '6051f7dba4e2070001537f4d',
    title: 'React Native Tutorial #10 - Todo App (part 2)',
    videoLink: 'SGEitne8N-Q',
  },
];

const FlatListExample = (props) => {

  const [visibleItemIndex, setVisibleItemIndex] = useState();
  const [focused, setFocused] = useState();

  const viewabilityConfig = {
    itemVisiblePercentThreshold: 40,
    waitForInteraction: true,
  };

  useEffect(() => {
    const subscribeFocusEvent = props.navigation.addListener('focus', () => {
      setFocused(true);
      console.log('focus', focused);
    });
    const subscribeBlurEvent = props.navigation.addListener('blur', () => {
      setFocused(false);
      console.log('blur', focused);
    });
    return (() => {
      subscribeFocusEvent;
      subscribeBlurEvent;
    });
  }, [focused]);
  
  
  const onViewableItemsChanged = useCallback(({ viewableItems, changed }) => {
    console.log({
      message: 'triggers change....1', 
      viewableItems, 
      changed,
      focused
    }, 'CHECK');
    if (changed && changed.length > 0) {
      setVisibleItemIndex(changed[0].index);
    }
  });

  const viewabilityConfigCallbackPairs = useRef([{ viewabilityConfig, onViewableItemsChanged }]);
  
  const renderItem = ({item, index}) => {
    return (
      <View key={item.id} style={styles.videoSec}>
        <Text style={styles.videoTitle}>{item.title}</Text>
        <TouchableOpacity
          activeOpacity={0.9}
          onPress={() => {
            setVisibleItemIndex(null);
            // props.navigation.navigate('Shop');
          }}>
          <YouTubeIFrame
            videoId={item.videoLink}
            height={230}
            play={index === visibleItemIndex}
            initialPlayerParams={{rel: false, controls: false, loop: true}}
          />
        </TouchableOpacity>
      </View>
    );
  };
  if(focused) {
    return (
      <View style={styles.container}>
        <FlatList
          style={{flex: 1}}
          data={data}
          renderItem={renderItem}
          keyExtractor={item => item.id}
          viewabilityConfigCallbackPairs={viewabilityConfigCallbackPairs.current}
        />
      </View>
    );
  }
  return null;
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#F7F7F7',
  },
  videoSec: {
    flex: 1,
    paddingVertical: 10,
    borderBottomColor: '#FCEABC',
    borderBottomWidth: 3,
    backgroundColor: '#FFFFFF',
  },
  videoTitle: {
    fontSize: 16,
    color: '#1B110A',
    paddingHorizontal: 10,
    marginBottom: 5,
  },
});

export default FlatListExample;
  1. getCurrentRouteName of screen and render flatList. so that way it will render only if it's same screen.
export const FlatListScreen = ({ navigation, route }) => {

    console.log(route.name);

    // this screen title
    if(route.name === 'VideoScreen') {
        return (
            <FlatList 
                {...props}
            />
        );
    }
    return null;
};

如果您需要任何说明,请随时告诉我。