Sorry I am a newbie in React and working on useEffect function. Getting above error "Maximum Update Depth Exceeded".
Problem Statement: I need to hide the icon based on visibility value. I have an object Location History which has multiple locations. I am binding locations on map based on coordinates and within circle showing all the locations. But I am not able to hide this within same circle radius.
Here is my code:
export const performLocationHistorySearch = () => async (dispatch) => {
consolehelper("====================================");
consolehelper("Executing History Search");
let locationdata= store.getState().global.locationhistory;
console.log("location history", locationdata);
const center: Location = {
lat: store.getState().global.searchLat,
lng: store.getState().global.searchLng,
};
consolehelper(center);
const radius = store.getState().global.searchRad;
if (locationdata !== null) {
console.log(locationdata);
let _locations =locationdata.locations.map((el) => {
const crowDistance = calculateCrowDistance(
{ lat1: parseFloat(el.latitude), lon1: parseFloat(el.longitude) },
{ lat2: center.lat, lon2: center.lng }
);
console.log(crowDistance);
consolehelper("====================================");
return {
...el,
_isVisible_:
radius >= crowDistance ,
__distance: crowDistance,
};
});
console.log(_locations.filter((el) => el._isVisible_));
dispatch(globalSlice.actions.setlocationRecords({
...locationdata,
locations:_locations
}));
}
};
Second piece of code calling locations, here it is:
useEffect(() => {
console.log("====use effect location history====");
if (locationhistory && searchRad && searchLat && searchLng) {
dispatch(performLocationHistorySearch());
console.log("====use effect location history====");
}
}, [
dispatch,
locationhistory,
searchLat,
searchLng,
searchRad,
]);
While calling above function getting Error "maximum update depth exceeded". Any help would be appreciated.
Thanks
Related
When I allow browser to get the Geolocation upon button Click, I am able to see the Latitude and Longitude. But when I block the site for accessing location, I am getting "Unable to retrieve your location" and Latitude and Longitude as well ... Here When I Block the site, I should get only message "Unable to retrieve your location" and Latitude and Longitude should not show...Please find the Screenshot as well
Here is my Code
const GeolocationButton = () => {
const [lat, setLat] = useState(null);
const [lng, setLng] = useState(null);
const [status, setStatus] = useState(null);
const getLocation = () => {
if (!navigator.geolocation) {
setStatus('Geolocation is not supported by your browser');
} else {
setStatus('Please allow brower to access your Location');
navigator.geolocation.getCurrentPosition((position) => {
setStatus(null);
setLat(position.coords.latitude);
setLng(position.coords.longitude);
}, () => {
setStatus('Unable to retrieve your location');
});
}
}
return (
<div className="App">
<button onClick={getLocation}>Get Location</button>
<h1>Coordinates</h1>
<p>{status}</p>
{lat && <p>Latitude: {lat}</p>}
{lng && <p>Longitude: {lng}</p>}
</div>
);
}
export default GeolocationButton
lat and lng are state variables, and state is, well, persistent. Consequently, when you set lat and lng, they retain those values, even if you later block access to location data.
There are various ways to resolve:
Since status, lat and lng are all interdependent, use a single state variable to store all three. This approach best realizes the dependent relationship between the variables.
const GeolocationButton = () => {
// could also initialize location to `{}`
const [location, setLocation] = useState({status:null,lat:null,lng:null});
const getLocation = () => {
if (! navigator.geolocation) {
// don't spread previous state into new state, and
// no need to explicitly set location.lat & .lng
setLocation({
status: 'Geolocation is not supported by your browser',
});
} else {
…
When setting status, also set lng and lat. This doesn't explicitly implement the interdependency between variables, and so requires more discipline on the part of the programmer.
if (! navigator.geolocation) {
setStatus('Geolocation is not supported by your browser');
setLat(null);
setLng(null);
} else {
// note: there's a typo in "browser" in the question sample
setStatus('Please allow browser to access your Location');
setLat(null);
setLng(null);
…
When getting the location, initialize lat and lng to null:
const getLocation = () => {
setLat(null);
setLng(null);
When displaying lat and lng, check that status isn't set (you should also check status before displaying it):
{ status ? <p>{status}</p>
{!status && lat && <p>Latitude: {lat}</p>}
or:
{ status
? <p>{status}</p>
: <>
{lat && <p>Latitude: {lat}</p>}
{lng && <p>Longitude: {lng}</p>}
</>
}
For all React Gurus! The logic is that I make a query to overpass and get some GeoJSON, now I need to pass this GeoJSON object to another Component (which is not its child) so that there I could make some calculations and show it on the screen.
The general structure is like this: There is a Main.js component which has two children MapBox.js and CalculationResults.js. MapBox.js has a child OverpassLayer.js which gives me GeoJSON. This GeoJSON I need to pass to CalculationResults.js. I tried to implement callback function all the way from parent Main.js but it always returns me the GeoJSON from the previous query. Could you please help me with the correct way of passing data between Components.
This is my OverpassLayer.js
const OverpassLayer = (props) => {
const [geojson, setGeojson] = useState();
useEffect(() => {
makeQuery();
}, [props.street, props.houseNumber]);
const makeQuery = () => {
const query = `[out:json];nwr["addr:street"="${props.street}"]["addr:housenumber"="${props.houseNumber}"][building](59.3518076,24.55017,59.5915769,24.9262831);out geom;`;
const options = {
flatProperties: true,
overpassUrl: "https://overpass-api.de/api/interpreter",
};
overpass(query, dataHandler, options);
};
const dataHandler = (error, osmData) => {
if (
!error &&
osmData.features !== undefined &&
osmData.features[0] !== undefined
) {
console.log(osmData.features[0]);
let area = (getArea(osmData.features[0].geometry.coordinates[0]));
console.log(area);
setGeojson(osmData);
}
};
function keyFunction(geojson) {
if (geojson.features.length === 0) {
return "";
} else {
return geojson.features[0].id;
}
}
function getArea(array) {
if (array) {
let arrayPolygon = array;
let polygon = turf.polygon([arrayPolygon]);
let area = turf.area(polygon);
return area;
}
return 0;
}
return geojson ? <GeoJSON key={keyFunction(geojson)} data={geojson} /> : null;
};
the easiest way is to store the data in localStorage. For the example, below setGeojson(osmData) you can write localStorage.setItem("Geojson", JSON.stringify(Geojson)); and in CalculationResults.js you can call it in useEffect() or componentDidMount():
const getGeojson = JSON.parse(localStorage.getItem("Geojson"));
if(getGeojson){
if(getGeojson.length > 0){
setGeojson(getGeojson);
}
}
the more advanced way is to use redux
when user get close to some area on map, display specific data from one other function? I am calculating average of each area estate prices in computed functions. it's below. you can see the average function on jsfiddle...
Already displaying averages but, what I need to do here, when user get zoomed in that region/city then display that areas average... The original code with map down below...
For example, how to sets bounds and connect those bounds to average function??? Thank you for helping.!
code updated!
data() {
return {
avg:"",
map: {},
mapName: "map",
estates: [],
},
mounted() {
axios.get('/ajax').then((response) => {
this.estates = response.data
});
this.initMap();
},
methods: {
initMap: function(){
var mapOptions =
{
zoom : 6,
center : {
lat:34.652500,
lng:135.506302
}
};
this.map = new google.maps.Map(document.getElementById(this.mapName), mapOptions);
google.maps.event.addListener(this.map, 'bounds_changed', function() {
console.log("bound changed alert");
});
},
avgArray: function (region) {
const sum = arr => arr.reduce((a,c) => (a += c),0);
const avg = arr => sum(arr) / arr.length;
return avg(region);
},
},
computed: {
groupedPricesByRegion () {
return this.estates.reduce((acc, obj) => {
var key = obj.region;
if (!acc[key]) {
acc[key] = [];
}
acc[key].push(obj.m2_price);
return acc;
}, {});
},
averagesByRegion () {
let arr = [];
Object.entries(this.groupedPricesByRegion)
.forEach(([key, value]) => {
arr.push({ [key]: Math.round(this.avgArray(value)) });
});
return arr;
},
},
I don't think this is specific to vue, its more about google-maps.
You can listen to the bounds_changed event on the map object: bounds_changed
And then get the boundaries of your current view
Have a look at this excellent answer which should help you out.
If you are using vuejs, you an look at this library vue-google-maps which should help you out.
P.S make sure to debounce the function you call on bounds_changed or you may make a lot of unnecessary calls to your generating-averages function
I'm not sure if this is a bug so I'm going to ask for advice first since I'm very new to ReactJS
I'm trying to implement Google Distance Matrix to get the distance.
(If there are any pre-built reactjs alternative solution, please let me know)
My Code:
import React, {Component} from 'react';
import GoogleMap from 'google-distance-matrix';
//...
class View extends Component {
state = {
//...
address:'',
dest: '',
distanceText:'',
openModal: false,
foundDistance: false,
distanceText: "",
address: "New York NY",
dest: "Montreal"
};
constructor (props){
super(props)
this.searchUpdated = this.searchUpdated.bind(this);
this.handleFormSubmit = this.handleFormSubmit.bind(this);
}
handleFormSubmit = (event) => {
const component = this
// const { address, dest } = this.state
let address = "Toronto, ON, CA"
let dest = "Vancouver, ON, CA"
let origins = ['San Francisco CA', '40.7421,-73.9914'];
let destinations = ['New York NY', 'Montreal', '41.8337329,-87.7321554',
'Honolulu'];
event.preventDefault()
// console.log(event)
GoogleMap.matrix(address, dest, function (err, distances) {
distance.key('AIzaSyCFKLGuYz6ffYby7U-ODjFtV5TO4nDyevE');
distance.units('imperial');
console.log("address");
console.log(dest);
console.log(err);
console.log(distances);
if (err) {
return console.log(err);
}
if(!distances) {
return console.log('no distances');
}
if (distances.status == 'OK') {
if(distances.rows[0].elements[0]) {
var distance = distances.rows[0].elements[0].duration['text'];
console.log(distance);
component.setState({
foundDistance: true,
distanceText: distance
});
}
}
}).bind(this);
}
componentWillMount() {
//...
}
componentDidMount () {
// ...
}
render() {
//...
return (
<div>
<button onClick={this.handleFormSubmit}>Hello </button>
</div>
)
}
}
export default View;
I literally just want to console.log() the distance between two locations but I'm unable to do so... Right now, it's giving me this error:
Uncaught TypeError: locations.join is not a function
at formatLocations (index.js:45)
What the error gives me:
The error is emanating from your handleFormSubmit function when you call GoogleMap.matrix, it should look like this:
handleFormSubmit = (event) => {
const component = this
// const { address, dest } = this.state
let address = ["Toronto, ON, CA"];
let dest = ["Vancouver, ON, CA"];
event.preventDefault()
// console.log(event)
GoogleMap.matrix(address, dest, function (err, distances) {
Notice the brackets for Toronto and Vancouver; the package expects those two arguments to be arrays, not strings
I'm trying to get the user's location (that works) and set it to the current state in a React component (this part doesn't). I've looked through a few answers on here and can't tell what I'm doing wrong.
Here's what I have:
class Container extends Component {
constructor() {
super()
this.state = {
location: {
lat: 0,
lng: 0
}
}
}
componentDidMount() {
navigator.geolocation.getCurrentPosition(
(position) => {
let lat = position.coords.latitude
let lng = position.coords.longitude
console.log("getCurrentPosition Success " + lat + lng) // logs position correctly
this.setState({
location: {
lat: lat,
lng: lng
}
})
},
(error) => {
this.props.displayError("Error dectecting your location");
console.error(JSON.stringify(error))
},
{enableHighAccuracy: true, timeout: 20000, maximumAge: 1000}
)
}
render() {
const location = this.state.location
return (
<div>
<Map center={location}/>
</div>
)
}
}
It looks similar to what other people have, and I've tried a few different ways, but I can't get the state to set. Is there something I'm missing or doing wrong?
The setState command is working fine.
Note that the get location is an async. call, and therefore the render() will be called twice. The first time it call, the lat/lng is zero.
You can add a logic to check it is zero and return null, if you want to render the output after getting the lat/lng from the geolocation services.