Custom annotation on mapbox
How can I achieve this using Mapbox and Annotations in React Native. I have tried nesting the annotations, rendering as polyline but am not getting the desired result. Can anyone help with resolving this?
Check out this working code. Finally was able to figure out the correct way to do it :
Inside my render() :
<Mapbox.MapView
ref={map => { this.map = map; }}
styleURL={Mapbox.StyleURL.Basic}
zoomLevel={15}
centerCoordinate={[11.256, 43.770]}
style={{flex: 1}}
showUserLocation={true}
userTrackingMode={Mapbox.UserTrackingModes.Follow}>
{this.state.markers.map(marker => (
<Mapbox.PointAnnotation
key= {marker.title}
id= {marker.title}
coordinate={marker.coordinates}>
<View style={styles.annotationContainer}>
<View style={styles.annotationFill} />
</View>
<Mapbox.Callout title={marker.title} />
</Mapbox.PointAnnotation>
))}
</Mapbox.MapView>
Function to update this.state.markers :
_getAnnotations = (key, location) => {
let newelement = {
title: key,
coordinates: location,
};
this.setState(prevState => ({
markers: [...prevState.markers, newelement]
}))
}
Geoquery trigger :
this.state.geoQuery.on("key_entered", (key, location, distance) => {
this._getAnnotations(key,location);
});
Related
I call data by using useQuery and gql.
const SEE_ALL_FEED_ORDER = gql`
query seeAllFeedOrder {
seeAllFeedOrder {
id
name
avatar
directFeedNumber
}
}
`;
const { data: allFeedData, loading: allFeedDataLoading,
refetch: allFeedRefetch } = useQuery(SEE_ALL_FEED_ORDER);
I named result data as allFeedData as above.
And I need to sort this allFeedData before screen is shown up.
So I use useEffect and useState.
const [flatlistdata, setFlatlistdata] = useState([]);
useEffect(() => {
if (!allFeedDataLoading) {
setFlatlistdata(
[...allFeedData.seeAllFeedOrder].sort(function (a, b) {
return b.directFeedNumber - a.directFeedNumber;
})
);
}
}, []);
So if this query loading is finished, then I put sorted data to flatlistdata by using setFlatlistdata.
And with this flatlistdata, I run flatlist.
<FlatList
data={flatlistdata}
keyExtractor={(item) => item.id}
renderItem={RankRow}
refreshing={refreshing}
onRefresh={refresh}
/>
But when I click screen, undefined is not an object(evaluating 'allFeedData.seeAllFeedOrder' error comes.
which means I couldn't call allFeedData.
I think this might happen screen is drawn before data is sorted? is that right?
So I also give condition to Flatlist as below.
flatlistdata === [] ? (
<View>
<ActivityIndicator size={30}></ActivityIndicator>
</View>
) :
<FlatList
data={flatlistdata}
keyExtractor={(item) => item.id}
renderItem={RankRow}
refreshing={refreshing}
onRefresh={refresh}
/> }
or
allFeedDataLoading ? (
<View>
<ActivityIndicator size={30}></ActivityIndicator>
</View>
) :
<FlatList
data={flatlistdata}
keyExtractor={(item) => item.id}
renderItem={RankRow}
refreshing={refreshing}
onRefresh={refresh}
/> }
I intended to draw ActivityIndicator screen first before data is sorted and put to flatlistdata and then proper screen show up, but it throws same error.
What is the problem? and how can I fix this ?
So I use useEffect and useState.
why would you need that? the sorted data is derived state that doesn't need to be managed separately:
const { data: allFeedData } = useQuery(SEE_ALL_FEED_ORDER);
const sortedData = allFeedData ? [...allFeedData.seeAllFeedOrder].sort(...) : []
then just pass sortedData to the FlatList. If it turns out that sorting is slow or that referential identity is important, you can wrap the sorting in useMemo. I also have a blog post on this topic: https://tkdodo.eu/blog/dont-over-use-state
I'm currently struggling in making my FlatList applying the changes I do to it. What I am wanting right now is that when I click an item in my flatlist, that it highlights in a certain color. I followed an approach done by a guy but I am having the problem that to me is not working the update once I click.
I can see through console that all I am doing performs a modification but I think that I am missing some point with extraData parameter since it is not re-rendering with the backgroundColor that I would like to apply.
The code I have is as following, I know that the style I am applying is correct since if i substitute in the map styles.list per styles.selected, everything gets the background I would like to be applied to the elements I click.
So summarizing, the issue I think I have is that the flatlist is not re-rendering so it doesn't show the modifications I perform on it. Any idea of what I am doing wrong? Any tip?
render() {
const { students, studentsDataSource, loading, userProfile } = this.props.navigation.state.params.store;
this.state.dataSource = studentsDataSource._dataBlob.s1.map(item => {
item.isSelect = false;
item.selectedClass = styles.list;
return item;
})
const itemNumber = this.state.dataSource.filter(item => item.isSelect).length;
return (
<View style={styles.container}>
<Item rounded style={styles.searchBar}>
<Input placeholder='Group Name'/>
</Item>
<FlatList
style={{
flex: 1,
width: "100%",
}}
data={this.state.dataSource}
ItemSeparatorComponent={this.FlatListItemSeparator}
renderItem={ ({ item }) => (
<ListItem avatar style={[styles.list, item.selectedClass]}
onPress={() => this.selectItem(item)}>
<Left>
{!item.voteCount && <Avatar unseen={true} /> }
{!!item.voteCount > 0 && <Avatar />}
</Left>
<Body>
<Text>{item.name}</Text>
<Text note>{item.group}</Text>
</Body>
</ListItem>
)
}
listKey={item => item.key}
extraData={this.state}
/>
</View>
);
}
Here we can find the state and SelectItem functions:
constructor(props) {
super(props)
this.state = {
dataSource : [],
}
}
//FlatListItemSeparator = () => <View style={styles.line} />;
selectItem = data => {
//{console.log("inside SelectItem=", data)}
data.isSelect = !data.isSelect;
data.selectedClass = data.isSelect? styles.selected: styles.list;
const index = this.state.dataSource.findIndex( item => data.key === item.key);
this.state.dataSource[index] = data;
this.setState({
dataSource: this.state.dataSource,
});
console.log("This state has the changes:=",this.state.dataSource)
};
Well the main issue was that I was not using the .setState and instead I was doing assignations which killed the listeners.
I am trying to build a Chat Application using react-native and using flatlist to display the messages. And i want my list to stay at bottom always can anyone plzz suggest me a solution.
Basically you need to use scrollToEnd on flatList..
First: create a flatList reference with:
ref={ (ref) => { this.myFlatListRef = ref } }
Second: add onContentSizeChange listener on flatList
onContentSizeChange={ () => { this.myFlatListRef.scrollToEnd({animated:true}) } }
Third: add onLayout listener on flatList for scroll to bottom when keyboard is showed.
onLayout={ () => { this.myFlatListRef.scrollToEnd({animated:true}) } }
eg:
<FlatList
ref={ (ref) => { this.myFlatListRef = ref } }
onContentSizeChange={ () => { this.myFlatListRef.scrollToEnd({animated:true}) } }
onLayout={ () => { this.myFlatListRef.scrollToEnd({animated:true}) } }
data={this.state.messages}
renderItem={ ({item}) => <Item data={item} /> }
/>
the simplest way to do this is to use inverted props in the flatlist
this allow us to render list up-side-down
for more ref. vidit https://facebook.github.io/react-native/docs/flatlist#inverted
Here, you can do this -
const messageData = this.props.convo
return (
<View style={{ flex: 1 }}>
<FlatList
inverted={-1}
style={styles.list}
extraData={this.props}
data={messageData}
keyExtractor = {(item) => {
return item.id;
}}
renderItem=
{this.renderItem}
/>
after that, you will get an inverted list which will have the topmost elements in the list, so for fixing this you can add reverse() in your flatlist data or in your reducer. Like this-
data={messageData.reverse()} or in reducer return { ...state, chats: action.payload.reverse() }
You can give a refto FlatList and access certain flatList properties that can scroll at particular position. Here below resolution should work.
setTimeout(() => this.refs.flatList.scrollToEnd(), 200)
ScrollToEnd React native
I'm currently learning react-native and react-native-maps (using typescript ES6) and I'm having some issues rendering markers in that they just don't appear. I've attempted to separate my logic into components that make's sense to me but I'm wondering if this is the reason I can't get them to render.
What I have is the following:
Main Map Component
<MapView style={styles.map}
region={this.props.region}
showsUserLocation={true}
followsUserLocation={false}>
<View>
<SearchCircle />
<MarkerList />
<View>
<MapView>
SearchCircle
The SearchCircle works perfectly and the code contains:
import { Circle } from 'react-native-maps';
...
class SearchCircle extends React.Component<SearchProps> {
render() {
return (
<Circle center={this.props.user.location}
radius={this.props.map.searchRadius}
fillColor={'rgba(230,238,255,0.5)'}
strokeColor={'#1A66FF'}
strokeWidth={1} />
);
}
}
MarkerList - ISSUE
The MarkerList is the component that does not render correctly. It contains:
class MarkerList extends React.Component<MarkerListProps> {
// render our contacts
render() {
return (
<View>
{this.props.markerlist[0] != undefined && this.props.markerList.map((marker, index) => (
this.renderMarker( marker, index )
))}
</View>
);
}
renderMarker( marker: MarkerEntry, index: number ) {
return (
<View key={index}>
<Marker coordinate={marker.location} title={marker.title} />
<Text>{'Lat: ' + marker.location.latitude + ', Long: ' + marker.location.longitude}</Text>
</View>
);
}
}
The MarkerList component does call render() when the state updates (when I update the markers locations) BUT only the Text element renders/updates correctly. The Lat/Lng is displayed correctly on my screen however the Marker element does not appear at all. The markerlist property for the MarkerList component is a list of type MarkerEntry which contains the following:
interface MarkerEntry {
title: string
location: LatLng
}
No matter what I try, I cannot get any markers to render from this array. If I statically define markers then everything works just fine but with my property array list map they never render.
Static Example that works
class MarkerList extends React.Component<MarkerListProps> {
// render our contacts
render() {
let marker = {
id: 'Test',
location: {
latitude: -31.970097415447232,
longitude: 115.92362245895334
}
};
let markerArray: any[] = [];
markerArray.push( marker );
return (
<View>
{markerArray[0] != undefined && markerArray.map((c, index) => (
<Marker key={index} coordinate={c.location} title={c.id} />
))}
</View>
);
}
}
Is anyone able to assist. I can't understand what I'm doing wrong or if I'm missing something about how react-native should be structured for this to work.
Thanks in advance!
So I've managed to solve my problem and thought I'd leave my final solution here for anyone else that needs it. The issues were with how I was defining the MapView and also with how I was returning the map results.
Map Component
For the main map component I had to remove the child <View> entry I had defined.
Before
<MapView style={styles.map}
region={this.props.region}
showsUserLocation={true}
followsUserLocation={false}>
<View>**
<SearchCircle />
<MarkerList />
<View>
<MapView>
After
<MapView style={styles.map}
region={this.props.region}
showsUserLocation={true}
followsUserLocation={false}>
<SearchCircle />
<MarkerList />
<MapView>
MarkerList
The second part I had to change was the render function and it's syntax in the render function.
Before
class MarkerList extends React.Component<MarkerListProps> {
// render our contacts
render() {
return (
<View>
{this.props.markerlist[0] != undefined && this.props.markerList.map((marker, index) => (
this.renderMarker( marker, index )
))}
</View>
);
}
renderMarker( marker: MarkerEntry, index: number ) {
return (
<View key={index}>
<Marker coordinate={marker.location} title={marker.title} />
<Text>{'Lat: ' + marker.location.latitude + ', Long: ' + marker.location.longitude}</Text>
</View>
);
}
}
After
Note the additional return statements outside and inside the map call and the removal of the <View> element.
class MarkerList extends React.Component<MarkerListProps> {
// render our contacts
render() {
return this.props.markerList.map((marker, index) => {
return( this.renderMarker( marker, index ));
});
}
renderMarker( marker: MarkerEntry index: number) {
return (
<View key={index}>
<Marker coordinate={marker.location} title={marker.title} />
</View>
);
}
}
I am trying to implement a collapsible box in react native.Its working fine for dummy data. But when i tried to list the data response from server i'm getting error.I'm using map method over the response for listing the details.But showing error evaluating this.state.details.map.Also i'm confused to where to place the map method.Below is the code that i've tried.I refer this doc for collapsible box.
Example
class DetailedView extends Component{
constructor(props){
super(props);
this.icons = {
'up' : require('../Images/Arrowhead.png'),
'down' : require('../Images/Arrowhead-Down.png')
};
this.state = {
title : props.title,
expanded : true,
animation : new Animated.Value()
};
}
toggle(){
let initialValue = this.state.expanded? this.state.maxHeight + this.state.minHeight : this.state.minHeight,
finalValue = this.state.expanded? this.state.minHeight : this.state.maxHeight + this.state.minHeight;
this.setState({
expanded : !this.state.expanded
});
this.state.animation.setValue(initialValue);
Animated.spring(
this.state.animation,
{
toValue: finalValue
}
).start();
}
_setMaxHeight(event){
this.setState({
maxHeight : event.nativeEvent.layout.height
});
}
_setMinHeight(event){
this.setState({
minHeight : event.nativeEvent.layout.height
});
}
state = {details: []};
componentWillMount(){
fetch('https://www.mywebsite.com' + this.props.navigation.state.params.id )
.then((response) => response.json())
.then((responseData) =>
this.setState({
details:responseData
})
);
}
render(){
let icon = this.icons['down'];
if(this.state.expanded){
icon = this.icons['up'];
}
return this.state.details.map(detail =>
<Animated.View
style={[styles.container,{height: this.state.animation}]}>
{detail.data.curriculum.map(curr =>
<View onLayout={this._setMinHeight.bind(this)}>
<Card>
<CardSection>
<View style={styles.thumbnailContainerStyle}>
<Text style={styles.userStyle}>
Hii
</Text>
</View>
<TouchableHighlight onPress={this.toggle.bind(this)}
underlayColor="#f1f1f1">
<Image style={styles.buttonImage} source={icon}></Image>
</TouchableHighlight>
</CardSection>
</Card>
</View>
<View style={styles.body} onLayout={this._setMaxHeight.bind(this)}>
{this.props.children}
<Card>
<CardSection>
<Text>{this.props.navigation.state.params.id}</Text>
</CardSection>
</Card>
</View>
)}
</Animated.View>
);
}
}
This is the screenshot for working code with dummy data
1. Solving the Error :
The API call you are making is asynchronous and once the API is called, the code continues to execute before getting the response from the API. The component tries to map through this.state.details before there are any details.
A solution here is that you need to set an ActicityIndicator/Loader initially when component is mounted and once you get the details/response from the API, the state changes and then you can map through this.state.details
Add empty details array to your initial state.
state = { details:[] }
Then put your return this.state.details.map(detail.... Inside an if condition like this
if(this.state.details.length > 0) {
<map here>
} else {
return <ActivityLoader />
}
2. Where to place the map methiod
You need to put it inside a function and call that function from within you render method.
showDetailsFunction() {
return this.state.details.map(detail =>
}
render() {
return(
{this.showDetailsFunction()}
)
}