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堆栈来查找问题的原因,调试了很多但还无法跟踪。
对于你的情况,我想出了两种方法来解决你的问题。 (打开以获得更好的解决方案)
- 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;
- 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;
};
如果您需要任何说明,请随时告诉我。
我遇到了来自平面列表的 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堆栈来查找问题的原因,调试了很多但还无法跟踪。
对于你的情况,我想出了两种方法来解决你的问题。 (打开以获得更好的解决方案)
- 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;
- 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;
};
如果您需要任何说明,请随时告诉我。