react-native dynamic activity indicator - react-native

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.

Related

Videos play at the same time - 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.

I have passed component as argument, now how to render the component?

Below in a code I have describe the question as comment
//This is MessagesScreen.js
<FlatList
data={messages}
keyExtractor={messages => messages.id.toString()}
renderItem={({ item }) =>
<ListItem style={styles.listItem}
title={item.title}
subTitle={item.description}
image={item.image}
DeleteIconView={ListItemDeleteAction} /*Here ListItemDeleteAction.js
is component want to pass it to
the ListItem.js. Note: I have
imported all the nessory
labries*/
onPress={() => handleDelete(item)}
//renderRightActions={ListItemDeleteAction}
/>
}
ItemSeparatorComponent={ListItemSeparator}
refreshing={refreshing}
onRefresh={() => {
setMessages([
{
id: 2,
title: 'Komail',
description: 'React-Native Developer',
image: require("../asserts/komail.jpg"),
}
])
}}
/>
This is ListItemDeleteAction.js, which I want to render in ItemList.js
//This is ListItemDeleteAction.js
function ListItemDeleteAction(props) {
return (
<View style={styles.container}>
<MaterialCommunityIcons name="trash-can" size={30} color={Colors.danger} />
</View>
);
}
Now, in ListItem.js I want to render the ListItemDeleteAction.js as I have passed as a argument. Below in code I have described as comment.
Note: I am strict to this method, render the ListItemDeleteAction as it passed as argument, which is "DeleteIconView" as parameter in ListItem.js
function ListItem({ image, title, subTitle, ImageComponent, style, onPress, DeleteIconView}) {
return (
<TouchableHighlight
//onPress={onPress}
underlayColor={Colors.light}
>
<View style={[styles.container, style]}>
{ImageComponent}
{image && <Image style={styles.image} source={image} />}
<View style={styles.parentDeatailContainer}>
<View style={styles.detailContainer}>
<Text style={{ fontWeight: "bold" }}>{title}</Text>
{subTitle && <Text>{subTitle}</Text>}
</View>
<TouchableOpacity style={styles.deleteIconContainer} onPress={onPress}>
{DeleteIconView} /* This is the place where I want to render the
ListItemDeleteAction components as I passed as argument but How? */
</TouchableOpacity>
</View>
</View>
</TouchableHighlight>
);
}
You are passing the prop wrong. When you write DeleteIconView={ListItemDeleteAction}, you aren't actually creating a JSX component. This can be solved by writing the following.
renderItem={({ item }) =>
<ListItem style={styles.listItem}
title={item.title}
subTitle={item.description}
image={item.image}
DeleteIconView={<ListItemDeleteAction />}
onPress={() => handleDelete(item)}
/>
}
Now, the prop DeleteIconView is an actual JSX component which can be rendered as usual.

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>

Modal doesnt open when clicking on TouchableOpacity - React Native

I am trying to implement the modal component in this app and for some reasons, I cant make it work. I have done it in another app and even though everything looks as it should in this one, it still doesn't work, it just doesn't toggle!
Here is my code (i call toogleModal() here ):
<TouchableOpacity
activeOpacity={1}
style={styles.slideInnerContainer}
//onPress={() => { alert(`You've clicked '${rest_name}'`); }}
onPress={() => this.toggleModal(rest_name)}
>
<View style={styles.shadow} />
<View style={[styles.imageContainer, even ? styles.imageContainerEven : {}]}>
{this.image}
<View style={[styles.radiusMask, even ? styles.radiusMaskEven : {}]} />
</View>
<View style={[styles.textContainer, even ? styles.textContainerEven : {}]}>
<View style={{ flexDirection: 'row' }}>
{uppercaseTitle}
{ratings}
</View>
<Text
style={[styles.subtitle, even ? styles.subtitleEven : {}]}
numberOfLines={2}
>
{rest_location}
</Text>
</View>
</TouchableOpacity>
Now here is the toggleModal() which should set the state and then call the onPressItem() :
toggleModal = (item) => {
this.setState({ isModalVisible: !this.state.isModalVisible });
this.onPressItem(item);
};
and onPressItem() :
onPressItem = (item) => {
return (
<ThemeProvider theme={theme}>
<Modal animationIn="rubberBand" animationOut={"bounceOut"}
isVisible={this.state.isModalVisible}
onBackdropPress={() => this.setState({ isModalVisible: false })}
>
<View style={{ flex: 1 }}>
{item}
</View>
<View style={{ flex: 1 }}>
<Button title="Hide modal" onPress={this.toggleModal} />
</View>
</Modal>
</ThemeProvider>
)
};
Now, remember this code is taken from another app where modal is working perfectly!
Most probably your issue with click option is connected with incorrect import TouchableOpacity from correct module. Check if you are importing from react-native:
import { TouchableOpacity } from 'react-native';
change this line
onPress={() => this.toggleModal(rest_name)}
to this:
onPress={() => {this.toggleModal(rest_name)}}
you only need to put the function call in brackets

Double Tap Button issue when keyBoard opens React native

I know there are already so many queries on this topic, I have tried every step but still won't be able to fix the issue.
Here is the code :
render() {
const {sContainer, sSearchBar} = styles;
if (this.props.InviteState.objectForDeleteList){
this.updateList(this.props.InviteState.objectForDeleteList);
}
return (
<View style={styles.mainContainer}>
<CustomNavBar
onBackPress={() => this.props.navigation.goBack()}
/>
<View
style={sContainer}
>
<ScrollView keyboardShouldPersistTaps="always">
<TextInput
underlineColorAndroid={'transparent'}
placeholder={'Search'}
placeholderTextColor={'white'}
selectionColor={Color.colorPrimaryDark}
style={sSearchBar}
onChangeText={(searchTerm) => this.setState({searchTerm})}
/>
</ScrollView>
{this.renderInviteUserList()}
</View>
</View>
);
}
renderInviteUserList() {
if (this.props.InviteState.inviteUsers.length > 0) {
return (
<SearchableFlatlist
searchProperty={'fullName'}
searchTerm={this.state.searchTerm}
data={this.props.InviteState.inviteUsers}
containerStyle={styles.listStyle}
renderItem={({item}) => this.renderItem(item)}
keyExtractor={(item) => item.id}
/>
);
}
return (
<View style={styles.emptyListContainer}>
<Text style={styles.noUserFoundText}>
{this.props.InviteState.noInviteUserFound}
</Text>
</View>
);
}
renderItem(item) {
return (
this.state.userData && this.state.userData.id !== item.id
?
<TouchableOpacity
style={styles.itemContainer}
onPress={() => this.onSelectUser(item)}>
<View style={styles.itemSubContainer}>
<Avatar
medium
rounded
source={
item.imageUrl === ''
? require('../../assets/user_image.png')
: {uri: item.imageUrl}
}
onPress={() => console.log('Works!')}
activeOpacity={0.7}
/>
<View style={styles.userNameContainer}>
<Text style={styles.userNameText} numberOfLines={1}>
{item.fullName}
</Text>
</View>
<CustomButton
style={{
flexWrap: 'wrap',
alignSelf: 'flex-end',
marginTop: 10,
marginBottom: 10,
width: 90,
}}
showIcon={false}
btnText={'Add'}
onPress={() => this.onClickSendInvitation(item)}
/>
</View>
</TouchableOpacity> : null
);
}
**I even tried with bellow code as suggested by #Nirmalsinh **:
<ScrollView keyboardShouldPersistTaps="always" style={sContainer}>
<CustomNavBar
onBackPress={() => this.props.navigation.goBack()}
/>
<TextInput underlineColorAndroid={'transparent'}
placeholder={'Search'}
placeholderTextColor={'white'}
selectionColor={Color.colorPrimaryDark}
style={sSearchBar}
onChangeText={(searchTerm) => this.setState({searchTerm})} />
{this.renderInviteUserList()}
</ScrollView>
I have followed this article:
https://medium.com/react-native-training/todays-react-native-tip-keyboard-issues-in-scrollview-8cfbeb92995b
I have tried with keyboardShouldPersistTaps=handled also but still, I have to tap twice on my Custom Button to perform an action. Can anybody tell me what I am doing wrong inside the code?
Thanks.
You need to add give value always in keyboardShouldPersistTaps to allow user tap without closing the keyboard.
keyboardShouldPersistTaps='always'
For example:
<ScrollView keyboardShouldPersistTaps='always'>
// Put your component
</ScrollView>
NOTE: Kindly put your tappable component inside the ScrollView. Otherwise it won't work.
You can use keyboardShouldPersistTaps='handled' in a ScrollView or Scrollables like FlatList SectionList etc. and embed a TouchableWithoutFeedBack to handle the case for dismiss on outside clicks.
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
<ScrollView keyboardShouldPersistTaps='handled'>
// Rest of the content.
</ScrollView/>
</TouchableWithoutFeedback>
For FlatList and SectionList you will have to handle KeyBoard.dismiss separately.
Please try this, It's working for me, it will works you also, i hope it helps...