I am building a google map that shows users the nearest locations. I have a json file for now, but will be used with a database later on. I need to show users locations based off a certain radius. Below is a component built trying to accomplish that. When I setState it only captures the last element in the array. Im not sure why.
Geocode.setApiKey("apikey");
Geocode.setLocationType("ROOFTOP");
const distances = [
{
value: 50,
label: "50",
},
{
value: 150,
label: "150",
},
{
value: 250,
label: "250",
},
{
value: 500,
label: "500",
},
];
//Gathers location and calculates distances then sets state of that distance
function Map() {
const classes = useStyles();
// sets state
const [selected, setSelected] = useState({});
const [center] = useState({ lat: 43.211243, lng: -112.413304 });
const [userLocation, setUserLocation] = useState({});
const [input, setInput] = useState("");
const [nearestPlace, setNearestPlace] = useState([]);
const [miles, setMiles] = React.useState(50);
// gets user location
useEffect(() => {
geocode();
}, []);
// Grabs users location
const geocode = async () => {
let location = input;
await axios
.get("https://maps.googleapis.com/maps/api/geocode/json", {
params: {
address: location,
key: "apikey",
},
})
.then((response) => {
const { lat, lng } = response.data.results[0].geometry.location;
setUserLocation({ lat, lng }); // sets user location
})
.catch((error) => {
console.log(error);
});
};
//calculation of lat and lng
function distance(lat1, lng1, lat2, lng2, miles) {
// miles optional
if (typeof miles === "undefined") {
miles = false;
}
function deg2rad(deg) {
return deg * (Math.PI / 180);
}
function square(x) {
return Math.pow(x, 2);
}
var r = 6371; // radius of the earth in km
lat1 = deg2rad(lat1);
lat2 = deg2rad(lat2);
var lat_dif = lat2 - lat1;
var lng_dif = deg2rad(lng2 - lng1);
var a =
square(Math.sin(lat_dif / 2)) +
Math.cos(lat1) * Math.cos(lat2) * square(Math.sin(lng_dif / 2));
var d = 2 * r * Math.asin(Math.sqrt(a));
if (miles) {
return d * 0.621371;
} //return miles
else {
return d;
} //return km
}
// this is where I get tripped up. Its not working properly
const handleSubmit = (e) => {
e.preventDefault();
geocode();
let nearest = [];
for (let i = 0; i < locations.length; i++) {
nearest.push(
distance(
userLocation.lat,
userLocation.lng,
locations[i].location.lat,
locations[i].location.lng,
"miles"
)
);
}
const result = nearest.map((item) => {
let closest = [];
if ((miles === "50" && item <= 50) || item <= 100) {
closest.push(item);
return closest;
}
});
result.forEach((el) => {
if (el === "undefined") {
return;
} else {
console.log(el) // this provides the correct values
setNearestPlace(el); // Here is where it wont set my state to the values
console.log(nearestPlace);
}
return el;
});
};
const onSelect = (item) => {
setSelected(item);
};
const handleChange = (event) => {
setMiles(event.target.value);
};
return (
<>
<LoadScript googleMapsApiKey="apikey">
<GoogleMap
className={classes.map}
mapContainerStyle={mapStyles}
zoom={7}
center={center}
>
{locations.map((item) => {
return (
<Marker
key={item.name}
position={item.location}
onClick={() => onSelect(item)}
/>
);
})}
{selected.location && (
<InfoWindow
position={selected.location}
clickable={true}
onCloseClick={() => setSelected({})}
>
<div>
{selected.image ? (
<img
alt="spudnik"
style={{ width: "200px" }}
src={selected.image}
/>
) : null}
<p>{selected.name}</p>
<p>{selected.address}</p>
<p>{selected.phone}</p>
</div>
</InfoWindow>
)}
</GoogleMap>
</LoadScript>
<div className={classes.formControl}>
<form
className={classes.form}
onSubmit={handleSubmit}
autoComplete="on"
>
<div>
<TextField
className={classes.input}
onChange={(e) => setInput(e.target.value)}
value={input}
size="small"
id="outlined-basic"
variant="outlined"
InputProps={{
endAdornment: (
<InputAdornment position="end">
<SearchIcon className={classes.searchIcon} />
</InputAdornment>
),
}}
/>
</div>
</form>
<div>
<Select
native
value={miles}
onChange={handleChange}
label="Age"
inputProps={{
name: "miles",
id: "outlined-age-native-simple",
}}
>
<option aria-label="None" value="" />
<option value={50}>50</option>
<option value={100}>100</option>
<option value={150}>150</option>
<option value={200}>200</option>
<option value={250}>250</option>
<option value={500}>500</option>
</Select>
</div>
</div>
</>
);
}
export default Map;
In here
result.forEach((el) => {
if (el === "undefined") {
return;
} else {
console.log(el) // this provides the correct values
setNearestPlace(el); // Here is where it wont set my state to the values
console.log(nearestPlace);
}
return el;
});
You are calling setNearestPlace()
in a loop and passing the array element as an argument. As a result, the last value passed, i.e. the last element of the array will be your final state.
I believe you are trying to do something like this instead
setNearestPlace(result.filter(ele => Boolean(ele))); // Boolean(ele) check whether the ele is null or undefined.