Passing parameter to custom hook - javascript

There is lot of documentation about the hooks, but I don't seem to understand them well enough.
Code
const App = () => {
const [markers, setMarkers] = useState(null);
const fetchMarkers = async () => {
const res = await axios.get(`${process.env.REACT_APP_BASE_URL}/markers`);
setMarkers(res.data);
};
useEffect(() => {
fetchMarkers();
}, []);
useInterval(() => {
fetchMarkers();
}, 10000);
}
I've made a custom hook which returns an object, I can call it like this:
const location = useGeoLocation();
I would like to add markers as parameter to useGeoLocation hook. This can be achieved easily just by calling the function on code and adding markers.
Objective
My goal is to make sure that markers contains some value before calling useGeoLocation. Right now, geoLocation hooks parameter returns value of null, because application didn't have time to fetchMarkers and update the state.
I've tried
My first thought was to do something like this:
if (markers) {
const location = useGeoLocation(markers);
}
But of course, like stated in rules of hooks, hooks can't be used conditionally.
UPDATED 10.1.2022 - useGeoLocation hook
Here I'm fetching users location. On success, I will compare users location to an array of markers & find the closest one to user.
On error, I will return only a predefined location.
This doesn't work because onSuccess is executed before there is any data in markers(null). I'm not sure how to use if - else statement here, because I'm also using useEffect & useState.
Can someone help?
const useGeoLocation = (markers) => {
const [location, setLocation] = useState({
loaded: false,
coordinates: { lat: "", lng: "" },
});
const onSuccess = (location) => {
// User location succesfully fetched, converting data format.
const targetPoint = turf.point([
location.coords.latitude,
location.coords.longitude,
]);
// Fetching data from markers parameter and converting data format.
var arr = [];
markers.map((marker) => arr.push(turf.point(marker.location)));
var points2 = turf.featureCollection(arr);
// Calculating which marker is nearest to targetPoint.
var nearest = turf.nearestPoint(targetPoint, points2);
// Setting location of nearest marker to usestate.
setLocation({
loaded: true,
coordinates: {
lat: nearest.geometry.coordinates[0],
lng: nearest.geometry.coordinates[1],
},
});
};
const onError = () => {
setLocation({
loaded: true,
coordinates: {
lat: 65.024335,
lng: 27.277089,
},
});
};
useEffect(() => {
if (!("geolocation" in navigator)) {
onError();
}
navigator.geolocation.getCurrentPosition(onSuccess, onError);
}, []);
return location;
};

Add markers in the dependency array of useEffect hook of useGeoLocation and call navigator.geolocation.getCurrentPosition(onSuccess, onError) if the markers are available and in the else part you can call setLocation().
const useGeoLocation = (markers) => {
const [location, setLocation] = useState({
loaded: false,
coordinates: { lat: "", lng: "" },
});
useEffect(() => {
if (markers) {
if (!("geolocation" in navigator)) {
onError();
}
navigator.geolocation.getCurrentPosition(onSuccess, onError);
} else {
setLocation();
}
}, [markers]);
return location;
};

Related

How can I do a push to an array and set the State with the data without causing a loop?

I'm doing a push to the array with the data, but when I set the array to the state I got several objects in the console, with the same content.
When the data clicked on map isn't equal to the first data on the array, the user still can click on other coordinates.
:
const [showPolygonForm, setShowPolygonForm] = useState(false)
const [polyData, setPolyData] = useState([])
const revealPolygonForm = () => {
setShowPolygonForm(!showPolygonForm)
}
const createPolygon = () => {
let arrayValores = []
if(showPolygonForm === true){
map.on('click', e => {
const data = {
lat: e.latlng.lat,
lng: e.latlng.lng
}
arrayValores.push(data)
setPolyData(arrayValores)
console.log(arrayValores)
})}
if(polyData[0] != polyData[(polyData.length - 1)]){
console.log("it's in")
map.on('click', e => {
arrayValores.push(e.latlng)
})}
}
createPolygon()

Wait until useState object has value

I've made a custom hook, which can be used to fetch users location and find nearest marker on map based on that information.
Right now it works, but first object it returns is useStates default value. Response looks like this:
{
coordinates: { lat: '', lng: '' },
loaded: false
}
After returning object first time, it starts to work like it should:
{
coordinates: { lat: 45.024335, lng: 19.277089 },
loaded: true
}
So basically I don't want that the hook sends empty objects, with no values. How can I fix this?
const useGeoLocation = (markers) => {
const [location, setLocation] = useState({
loaded: false,
coordinates: { lat: "", lng: "" },
});
const onSuccess = (location) => {
// User location succesfully fetched, converting data format.
const targetPoint = turf.point([
location.coords.latitude,
location.coords.longitude,
]);
// Fetching data from markers parameter and converting data format.
var arr = [];
markers.map((marker) => arr.push(turf.point(marker.location)));
var points2 = turf.featureCollection(arr);
// Calculating which marker is nearest to targetPoint.
var nearest = turf.nearestPoint(targetPoint, points2);
// Setting location of nearest marker to usestate.
setLocation({
loaded: true,
coordinates: {
lat: nearest.geometry.coordinates[0],
lng: nearest.geometry.coordinates[1],
},
});
};
const onError = () => {
setLocation({
loaded: true,
coordinates: {
lat: 65.024335,
lng: 27.277089,
},
});
};
useEffect(() => {
if (markers) {
if (!("geolocation" in navigator)) {
onError();
}
navigator.geolocation.getCurrentPosition(onSuccess, onError);
}
}, [markers]);
return location;
};
Instead of just returning the location object as a whole, you could split the return into an object with values for the data, a loading state, an error message if needs be. For example:
const useGeoLocation = (markers) => {
const [location, setLocation] = useState(undefined);
const [error, setError] = useState("");
const onSuccess = (location) => {
// Rest of the success handler...
setLocation({
coordinates: {
lat: nearest.geometry.coordinates[0],
lng: nearest.geometry.coordinates[1],
},
});
};
const onError = () => {
setLocation({
coordinates: {
lat: 65.024335,
lng: 27.277089,
},
});
setError("Some error message");
};
useEffect(() => {
if (markers) {
if (!("geolocation" in navigator)) {
onError();
}
navigator.geolocation.getCurrentPosition(onSuccess, onError);
}
}, [markers]);
return {
data: location,
isLoading: !location && !error,
error: error,
};
};
Then when calling the hook you can do:
const {data, isLoading, error} = useGeoLocation();
and check for the value of data, or that isLoading and error are false, before doing anything.

How to get nearby queries / restaurants from firestore using react native (GeoFirestore)

So i'm trying to get retrieve nearby restaurents based on user location. I'm using Geofirestore library to attempt to query nearby restaurent but with no luck. It just creates a document with fields like geohash,lat,long and store. and when i console log the nearby query i get an empty array? Code is below
export default () => {
const [restaurantsList, setRestaurantsList] = useState([]); //Initialise restaurant list with setter
const [errorMessage, setErrorMessage] = useState("");
const [lat, setLat] = useState(0);
const [lng, setLng] = useState(0);
const[nearbyLocations, setnearbyLocations] = useState(null);
//const type = route.params.('type', 'anonymous')
console.log(restaurantsList)
console.log(lat, lng)
console.log(nearbyLocations)
// // Initialize the Firebase SDK
// firebase.initializeApp({
// // ...
// });
const getUserLocation = () => {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition((position) => {
setLat(position.coords.latitude);
setLng(position.coords.longitude);
});
}
}
const getNearbyRestaurents = () => {
//Create a Firestore reference
const firestore = firebase.firestore();
// Create a GeoFirestore reference
const GeoFirestore = geofirestore.initializeApp(firestore);
// Create a GeoCollection reference
// Firebase reference where GeoFirestore will store its information
const geocollection = GeoFirestore.collection('user_location');
//Add a GeoDocument to a GeoCollection
geocollection.add({
name: 'Geofirestore',
score: 100,
// The coordinates field must be a GeoPoint!
coordinates: new firebase.firestore.GeoPoint(lat, lng)
})
// Create a GeoQuery based on a location
const query = geocollection.near({ center: new firebase.firestore.GeoPoint(lat, lng), radius: 20 });
// Get query (as Promise)
query.get().then((value) => {
// All GeoDocument returned by GeoQuery, like the GeoDocument added above
console.log(value.docs);
setnearbyLocations(value.docs)
});
}
const getRestaurants = async () => {
try {
const list = [];
var snapshot = await firebase.firestore().collection("Restaurants").get(); // gets data from the restaurents table
console.log("Here");
snapshot.forEach((doc) => { // iterates through each document in the table and push
list.push(doc.data());
});
setRestaurantsList([...list]);;
} catch (e) {
setErrorMessage(
"There's nae bleeding restaurants, I told you to upload them!"
);
}
};
//Call when component is rendered
useEffect(() => {
getRestaurants();
}, []);
So My goal is to return all nearby restaurents based on users locations. I have two fields called 'Latitude' and 'Longitude' in each restaurent collections

React won't loop through state in render

I'm fairly new to React and I have run into a problem. I need to gather data from an API and then use the data to create Polygons with Google Maps. However, I'm experiencing problems accessing it.
When I log this.state.coordinates:
Console.log result of this.state.coordinates
However, when I try to log this.state.coordinates[0] it shows undefined. My guess is that the order in which the data binds is wrong, but I can't seem to find the solution. Here is my code:
constructor(props) {
super(props);
this.state = {
coordinates: [{}],
loading: 'initial'
};
}
componentDidMount() {
this.setState({
loading: 'true'
})
this.getData()
}
getData = async () => {
let coordinates = []
let res = await axios.get('https://smartcity-parking-api.herokuapp.com/sectors')
const sectors = await res.data.data
sectors.map(async (sector, i) => {
let res = await axios.get(sector.self_links.detail)
const coordinateArray = await res.data.data.coordinates
coordinates[i] = {
latlng: [],
id: res.data.data.sector_data.sector_id
}
coordinateArray.map(coordinate => {
let latlng = {
lat: coordinate.latitude,
lng: coordinate.longitude
}
coordinates[i].latlng.push(latlng)
})
coordinates[i].latlng.push({
lat: coordinateArray[0].latitude,
lng: coordinateArray[0].longitude
})
})
this.setState({
coordinates: coordinates,
loading: 'false'
})
}
render() {
let { coordinates, loading } = this.state
if (loading === 'initial') {
return <h2>Intializing...</h2>;
}
if (loading === 'true') {
return <h2>Loading...</h2>;
}
if (loading === 'false') {
return (
//Google maps here
);
}
I have tried making a loader, but it doesn't seem to be working. Any help would be very appreciated!
Why not set coordinates to null initially in your constructor and then have your conditional be
(loading === 'false' && coordinates)
That may solve your issue.
Change getData function to this.
getData = async () => {
let coordinates = []
let res = await axios.get('https://smartcity-parking-api.herokuapp.com/sectors')
const sectors = res.data.data
const sectorsLinks = await Promise.all(sectors.map(sector => axios.get(sector.self_links.detail)))
sectorsLinks.map((sector, i) => {
const coordinateArray = sector.data.data.coordinates;
coordinates[i] = {
latlng: [],
id: sector.data.data.sector_data.sector_id
}
coordinateArray.map(coordinate => {
let latlng = {
lat: coordinate.latitude,
lng: coordinate.longitude
}
coordinates[i].latlng.push(latlng)
})
coordinates[i].latlng.push({
lat: coordinateArray[0].latitude,
lng: coordinateArray[0].longitude
})
});
console.log(coordinates);
this.setState({
coordinates: coordinates,
loading: 'false'
})
};

Firesbase firestore not working + Vuejs

I want to store my geopoints to firestore database but it wont work.
I created an array called acctPosition to store there the geopoints inside,
but in firebase i can't see the data, it looks like that nothing is stored.
How can I store the geopoints correctly?
The output:
The Geoloaction component:
data () {
coords: {
latitude: null,
longitude: null
},
acctPosition: []
},
...
mounted () {
var self = this;
// Try HTML5 geolocation.
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function(position) {
var pos = {
lat: position.coords.latitude,
lng: position.coords.longitude
};
console.log(pos)
var acctPos = self.acctPosition.push(pos)
console.log(acctPos)
}
} else {
// Browser doesn't support Geolocation
}
let docId = `${this.currentUser.uid}`
// store geoData to currentUser in firebase firestore
fb.usersCollection.doc(docId).set({
acctPosition: this.acctPos
}).then(ref => {
console.log('work')
}).catch(err => {
console.log(err)
})
}
Javascript is asynchronous, so fb.usersCollection.doc(docId).set() is going to be executed before getCurrentPosition is finished.
You can push to firebase after getCurrentPosition is done:
mounted () {
...
var self = this;
// Try HTML5 geolocation.
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function(position) {
var pos = {
lat: position.coords.latitude,
lng: position.coords.longitude
};
console.log(pos)
map.setCenter(pos);
var acctPos = self.acctPosition.push(pos)
console.log(acctPos)
let docId = `${self.currentUser.uid}`
// store geoData to currentUser in firebase firestore
fb.usersCollection.doc(docId).set({
acctPosition: self.acctPosition
}).then(ref => {
console.log('work')
}).catch(err => {
console.log(err)
})
}
} else {
// Browser doesn't support Geolocation
}
}

Categories