Prevent react-native-map from re-rendering after states change [Reat-Native] - react-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>

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.

Image does not show in my react-native-maps call out component. It only shows an empty rectangle?

I want to add Images to my call out component in react-native-maps-markers. But it only shows empty rectangles. How can I fix this? Without putting image component inside a Text component. Because then its harder to align images.
return (
<View style={styles.container} >
<MapView
Provider= {PROVIDER_GOOGLE}
ref={map => this._map = map}
showsUserLocation={true}
style={styles.map}
initialRegion={this.state.initialPosition}
customMapStyle={mapStyle}>
{
this.state.coordinates.map((marker, index) => (
<Marker
key={marker.name}
ref={ref => this.state.markers[index] = ref}
//onPress={() => this.onMarkerPressed(marker, index)}
coordinate={{latitude: marker.latitude, longitude: marker.longitude}}
title={'Safe Parking'}>
<Image
source={require('../icons/park.jpg')}
style={styles.icon}/>
<Callout>
<Image
source={marker.image}
style={styles.image}
/>
</Callout>
</Marker>
))
}
</MapView>
</View>
);
}
};
Have you set an height and width to your image ?
If yes, try to import your image instead of require like
import parkIcon from '../icons/park.jpg'
<Image source={parkIcon}

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

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.