Videos play at the same time - React Native - react-native

I have two videos from https://www.npmjs.com/package/react-native-video
I do not have the controls active, my intention is to activate and deactivate the videos by pressing buttons. I do it with states, my problem is that when I press a button to pause or play a video, all of them play, not just one.
I have a list of videos in a JSON and iterate through all of them.
Here is a snippet of my code:
const [paused, setPaused] = useState(false);
const playVideo = () => {
setPaused(!paused);
}
{videos.map((video) => (
<Video
source={{ uri: video.video }}
rate={1.0}
volume={1.0}
resizeMode="cover"
style={styles.video}
paused={paused}
onEnd={() => setPaused(true)}
/>
{paused && (
<View style={styles.videoPause}>
<Text style={styles.title}>{video.titulo}</Text>
<Text style={styles.description}>{video.descripcion}</Text>
<TouchableOpacity style={styles.playButton} onPress={() => playVideo()}>
<CustomIcon name="play" size={90} color={'#fff'} />
</TouchableOpacity>
</View>
)}
))}

The problem is with your state, you should make a component to wrap the Video and manage play/pause state in there. This way you can control every video individually.

First create a component which has independent state and not global as you have it here.
Example:
export default function VideoPlayer(props) {
const { styles, video } = props
const [paused, setPaused] = useState(false)
const playVideo = () => {
setPaused(!paused)
}
return (
<View>
<Video
paused={paused}
rate={1.0}
resizeMode="cover"
source={{ uri: video.video }}
style={styles.video}
volume={1.0}
onEnd={() => setPaused(true)}
/>
{
paused && (
<View style={styles.videoPause}>
<Text style={styles.title}>{video.titulo}</Text>
<Text style={styles.description}>{video.descripcion}</Text>
<TouchableOpacity style={styles.playButton} onPress={() => playVideo()}>
<CustomIcon color="#fff" name="play" size={90} />
</TouchableOpacity>
</View>
)
}
}
)
and then render it in your page like this:
{videos.map((video) => <VideoPlayer styles={...YOUR_STYLES} video={video}/>}
You can also declare the styles inside the component if you prefer but then you wont need to pass them as props.

Related

Use Modal in ReactNative

I am making an android app with ReactNative. When a button is clicked, an alert shows up with the result (My code is Alert.alert(''Results:", returnResults(a,b,c)). Is there a way I can use Modal and the function returnResults(a,b,c) with it to show the message in a more customised way?
The problem is that when I use normal alert, the text is different on every device and I want to prevent that.(I want to specify the font, its size etc)
const Modal = () => {
const [modal, setModal] = useState(false);
return (
<View>
<Button title="Open Modal" onPress={() => setModal(true)} />
<Modal
animationType="slide"
transparent={false}
visible={modal}
onRequestClose={() => setModal(false)}
>
<View style={{ marginTop: 22 }}>
<View>
<Text>This is the modal content</Text>
<Button title="Close" onPress={() => setModal(false)} />
</View>
</View>
</Modal>
</View>
);
};
I actually found what i had been looking for. I just had to write the following code:
{returnResults(a,b,c)}

React Native - add specific clearButton on input field when the keyboard is open

I am trying to create a specific clear button to use on both ios and android devices. I have created a reusable component for the several fields I have. When I press the fields since the keyboard opens the X button shows in all fields not only the field I have pressed. In the code below emptyField is a value set in a parent component.
const [keyboardShow, setKeyboardShow] = useState<boolean>(false);
useEffect(() => {
const showKeyboard = Keyboard.addListener('keyboardDidShow', () => {
setKeyboardShow(true);
});
const hideKeyboard = Keyboard.addListener('keyboardDidHide', () => {
setKeyboardShow(false);
});
return () => {
showKeyboard.remove();
hideKeyboard.remove();
};
}, []);
<TouchableComponent>
<TextInput
key={currencyTypeId}
ref={forwardRef}
style={styles.input}
onChangeText={onChangeText}
value={inputValue}
editable={editable}
autoCorrect={false}
autoCompleteType='off'
returnKeyType={returnKeyType}
placeholder={placeholder}
placeholderTextColor={placeholderColor}
keyboardType={keyboardType}
/>
</TouchableComponent>
{inputValue.length > 0 && keyboardShow && (
<View style={styles.customButton}>
<TouchableOpacity onPress={emptyField}>
<CloseIcon width={12} height={12}/>
</TouchableOpacity>
</View>
)}
Seems 'keyboardDidShow' and 'keyboardDidHide' events triggered in each reusable component.
You can try another approach. Just use onBlur and onFocus events. It's isolated for each component:
<TouchableComponent>
<TextInput
onBlur={() => setIsFocused(false)}
onFocus={() => setIsFocused(true)}
key={currencyTypeId}
ref={forwardRef}
style={styles.input}
onChangeText={onChangeText}
value={inputValue}
editable={editable}
autoCorrect={false}
autoCompleteType="off"
returnKeyType={returnKeyType}
placeholder={placeholder}
placeholderTextColor={placeholderColor}
keyboardType={keyboardType}
/>
</TouchableComponent>
{inputValue.length > 0 && isFocused && (
<View style={styles.customButton}>
<TouchableOpacity onPress={() => {}}>
<CloseIcon width={12} height={12} />
</TouchableOpacity>
</View>
)}

Prevent react-native-map from re-rendering after states change [Reat-Native]

In my app I'm using react-native-maps and every time states change the map reloads. How can I avoid this behaviour? I've tried to use React.memo but it seems to don't work.
[update]
I've tried to put the MapView directly inside the return and it doesn't reload, why?
Feel free to link me everything. Here's my code:
[functional component]
const MapViewContainer = () => {
return (
<MapView
initialRegion={initialRegion}
provider="google"
ref={mapRef}
style={styles.map}
>
{/* {items.map((item, index) => {
return <Marker {...{ item, index }} />;
})} */}
</MapView>
);
};
const MemorizeMap = React.memo(MapViewContainer, areEqual);
return (
<View style={styles.container}>
<MemorizeMap />
<Button
title="update"
style={{ flex: 1 }}
onPress={() => {
setOpacity(0);
}}
/>
</View>

Pausing videos on FlatList horizontal when swiping

I'm able to console log the viewable items, when swiping when using FlatList, but I was wondering how I can manage pausing the video. Or if there's a better way in doing so?
This is the RenderItem function component
const RenderItem = (props) => {
const [paused, setPaused] = useState(true);
const togglePlay = () => setPaused(prev => !prev);
return (
<View>
{props.is_video ? (
<>
<Video
paused={paused}
repeat
source={{uri: props.mediaUrl}}
/>
<TouchableWithoutFeedback onPress={togglePlay}>
<View>
{paused ? <Icon
size={180}
name="play"
type="FontAwesome"
style={{opacity: 0.7, color: '#cccccc'}}
/> : null}
</View>
</TouchableWithoutFeedback>
</>
) : (
<Image source={{uri: props.mediaUrl}} />
)}
</View>
);
};
Then in another function, I have this:
const Post = (props) => {
const onViewRef = useRef((viewableItems)=> {console.log(viewableItems)});
const viewConfigRef = useRef({ viewAreaCoveragePercentThreshold: 50 });
return (
<View style={{flex: 1}}>
<View>
<FlatList
onViewableItemsChanged={onViewRef.current}
viewabilityConfig={viewConfigRef.current}
data={props.navigation.state.params.media}
snapToAlignment={'center'}
horizontal
decelerationRate={'fast'}
pagingEnabled
renderItem={({item}) => <RenderItem {...item} />}
keyExtractor={item => item.mediaUrl}
/>
</View>
</View>
);
};
I'm able to press the video and it'll Play or Pause. When I'm swiping right now, the videos continue to play. I want to be able to ensure that when swiping, the video played will become paused now.
You can lift the pause/playing state to the parent component (Post). Since at most 1 video should be playing at anytime, the state can simply store the item ID (or mediaUrl if you are using that as the key/ID) that is currently playing.
In RenderItem:
<Video paused={props.paused} ... />
<TouchableWithoutFeedback onPress={props.onTogglePlay}>
In Post (you can use useCallback for the anonymous functions):
const [activeVideo, setActiveVideo] = useState(null);
...
<FlatList onViewableItemsChanged={() => setActiveVideo(null)} .../>
...
<RenderItem
paused={activeVideo !== item.mediaUrl}
onTogglePlay={() =>
setActiveVideo(item.mediaUrl === activeVideo ? null : item.mediaUrl)
}
{...item}
/>
You may also store the ref to the active video and pause the video through that ref.

react-native dynamic activity indicator

I have multiple videos from an array map that each have an activity indicator. I want to set to false once the video is loaded.
Can someone please explain a method where I can do this for each video individually. At the moment on the onLoad callback it's just setting a global state value.
{revArray.map((camera,index) => (
<View key={'camera'+index} style={{backgroundColor:'#eeeeee'}}>
<TouchableOpacity onPress={() => {this.removePrompt(camera.title.toString())}} style={{padding:6,position:'absolute',right:4,top:8,zIndex:999}}><Image source={require('../Images/delete1.png')} style={{width:16,height:16}}></Image></TouchableOpacity>
<Text key={'title'+index} style={{color:'#113b92',textAlign:'center',padding:10,fontSize:16,fontFamily:'Lato-Bold'}}>{camera.title}</Text>
<View style={{backgroundColor:'#000'}}>
{this.state.isLoading ? ( <View style={{ position: 'absolute',left: 0,right: 0,top: 0,bottom: 0,alignItems: 'center',justifyContent: 'center',zIndex:9999}}>
<ActivityIndicator size="large" color={'#000'} style={{backgroundColor:'#fff', padding:6, borderRadius:30}} />
</View>
): (null)}
<Video
onLoad={()=>{this.setState({isLoading:false})}}
key={'video'+index}
ref={(ref: Video) => { this.video = ref }}
style={styles.fullScreen}
resizeMode='cover'
source={{uri: camera.video+'?'+new Date()}}
repeat={true}
/>
</View>
</View>
))}
Because isLoading is the state of the component rendering each of the videos in the array its only ever going to control all of them... What you want to do here is render a "container" component with its own state for each of these -
class VideoWrapper extends React.Component {
state = {
isLoading: true
}
<View key={'camera'+index} style={{backgroundColor:'#eeeeee'}}>
...
<Video
onLoad={()=>{this.setState({isLoading:false})}}
key={'video'+index}
ref={(ref: Video) => { this.video = ref }}
style={styles.fullScreen}
resizeMode='cover'
source={{uri: camera.video+'?'+new Date()}}
repeat={true}
/>
</View>
}
And to render -
{revArray.map((camera,index) =>
<VideoWrapper key={camera.id} camera={camera} index={index} />
)}
This way each video in the array controls its own state.