I have a react functional-based component that needs some pieces of information lat and lng to pass into my Autocomplete component. I am trying to get lat and lng from an inner function but i couldnt. I tried doing
{ lat, lng } = getLatLng(results[0])
but it throws some syntax error, so I also tried
lat = getLatLng(results[0].lat)
lng = getLatLng(results[0].lng)
but it doesnt seem to work. This seems like such a simple issue but i cant figure out why. I tried putting the var keyword in front as well but it didnt work, nor did i expect it to. Below is my whole code for the component.
import usePlacesAutocomplete, { getGeocode, getLatLng } from "use-places-autocomplete"
import { useDispatch } from "react-redux";
import { setOrigin, setDestination } from '../../Slices/originDestinationSlice'
import TextField from '#mui/material/TextField';
import Autocomplete from '#mui/material/Autocomplete';
export const StartPlaces = () => {
const dispatch = useDispatch()
const { ready, value, setValue, suggestions: { status, data }, clearSuggestions } = usePlacesAutocomplete();
const handleSelect = async (val, selectedValue) => {
setValue(val, true);
// clearSuggestions();
const results = await getGeocode({ address: val });
// { lat, lng } = getLatLng(results[0])
lat = getLatLng(results[0].lat)
lng = getLatLng(results[0].lng)
}
return <>
<h4>Enter Origin</h4>
<Autocomplete
id="free-solo-demo"
freeSolo
options={data.map(({ description }) => description)}
onChange={(event, value) => {
dispatch(setOrigin({
coordinates: { lat, lng },
name: value
}))
}}
renderInput={(params) => <TextField {...params} label="Origin" onChange={(e) => handleSelect(e.target.value)} placeholder="e.g. Las Vegas" />}
sx={{ width: 300 }}
/>
</>
}
Any help would be greatly appreciated. thank you in advance!!
you should save your lat e lng in a state to use them correctly
try to change your code like this
import usePlacesAutocomplete, { getGeocode, getLatLng } from "use-places-autocomplete"
import React, {useState} from 'react'
import { useDispatch } from "react-redux";
import { setOrigin, setDestination } from '../../Slices/originDestinationSlice'
import TextField from '#mui/material/TextField';
import Autocomplete from '#mui/material/Autocomplete';
export const StartPlaces = () => {
const dispatch = useDispatch()
const { ready, value, setValue, suggestions: { status, data }, clearSuggestions } = usePlacesAutocomplete();
const [lat, setLat] = useState(0)
const [lng, setLng] = useState(0)
const handleSelect = async (val, selectedValue) => {
setValue(val, true);
// clearSuggestions();
const results = await getGeocode({ address: val });
const {lat, lng} = getLatLng(results[0])
setLat(lat)
setLng(lng)
}
return <>
<h4>Enter Origin</h4>
<Autocomplete
id="free-solo-demo"
freeSolo
options={data.map(({ description }) => description)}
onChange={(event, value) => {
dispatch(setOrigin({
coordinates: { lat, lng },
name: value
}))
}}
renderInput={(params) => <TextField {...params} label="Origin" onChange={(e) => handleSelect(e.target.value)} placeholder="e.g. Las Vegas" />}
sx={{ width: 300 }}
/>
</>
}
Related
I have just started learning TypeScript, and have been working on a small web app.
Due to state management, I have created a file called CountdownContext.tsx
import { useState, createContext, useContext, SetStateAction } from "react";
import { CountdownContextType } from "./types";
const CountdownContext = createContext({});
export const useCountdownContext = () => {
return useContext(CountdownContext) as CountdownContextType;
};
// React.useContext(TodoContext) as TodoContextType;
interface Props {
children: React.ReactNode;
}
export const CountdownContextProvider: React.FC<Props> = ({ children }) => {
const [eventDate, setEventDate] = useState<Date>(new Date());
function handleEventDate(e: React.ChangeEvent<HTMLInputElement>) {
setEventDate(new Date(e.target.value));
}
return (
<CountdownContext.Provider value={{ eventDate, handleEventDate }}>
{children}
</CountdownContext.Provider>
);
};
And I imported eventDate and handleEventDate in the file below.
import React, { useState } from "react";
import { useCountdownContext } from "../../contexts/CountdownContext";
type Props = {
activeEdit: boolean;
};
const EventDate: React.FC<Props> = ({ activeEdit }) => {
const { eventDate, handleEventDate } = useCountdownContext();
return (
<div>
{activeEdit ? (
<input type="date" onChange={(e) => handleEventDate(e.target.value, typeof new Date(e.target.value))} />
) : (
<div>{eventDate}</div>
)}
</div>
);
};
export default EventDate;
However, I got an error when importing. After searching about the problem happened,
I figured out that I needed to declare types.
Then I created a file below
export type CountdownContextType = {
eventDate: Date;
handleEventDate: (e: React.ChangeEvent<HTMLInputElement>) => void;
};
However, one concern is that I thought I already provided and declared types for eventDate and handleEventDate in the context file.
Thus, I thought to question why I gotta do this twice??
Please give me a clarification about this problem. Thanks so much for your kindness and help.
One way to not using types is to create initial value for your context like this:
import * as React from "react";
import ReactDOM from "react-dom";
import { useState, createContext, useContext } from "react";
const initialValue = {
eventDate: new Date(),
handleEventDate: (e: React.ChangeEvent<HTMLInputElement>) => {},
};
const CountdownContext = createContext(initialValue);
const useCountDownState = () => {
const [eventDate, setEventDate] = useState<Date>(new Date());
function handleEventDate(e: React.ChangeEvent<HTMLInputElement>) {
console.log("setting date");
setEventDate(new Date(e.target.value));
}
return { eventDate, handleEventDate };
};
const CountdownContextProvider = ({ children }: { children: JSX.Element }) => {
return (
<CountdownContext.Provider value={useCountDownState()}>
{children}
</CountdownContext.Provider>
);
};
const useCountdownContext = () => useContext(CountdownContext);
const EventDate = ({ activeEdit }: { activeEdit: boolean }) => {
const { eventDate, handleEventDate } = useCountdownContext();
return (
<div>
{activeEdit ? (
<input type="date" onChange={handleEventDate} />
) : (
<div>{eventDate.toDateString()}</div>
)}
</div>
);
};
function App() {
return (
<CountdownContextProvider>
<EventDate activeEdit />
</CountdownContextProvider>
);
}
ReactDOM.render(<App />, document.querySelector("#root"));
The code is heavily inspired by:
https://youtu.be/P95DuIBwnqw?t=822
And here is the source code from the link:
https://github.com/jherr/which-react-state-manager/blob/main/examples/hooks-context/src/store.tsx
I am using MUI aocomplete component.
I have called an API call and feeding 'options' and 'value' with API response.
Following is the code.
// App.js
import { useEffect, useCallback, useState } from "react";
import TextField from "#mui/material/TextField";
import Autocomplete from "#mui/material/Autocomplete";
function App() {
const [getUsers, setUsers] = useState([]);
const fetchUsers = useCallback(async () => {
try {
const response = await fetch(
"https://jsonplaceholder.typicode.com/users"
);
const userData = await response.json();
setUsers(userData);
} catch (error) {
console.log("Error in catch!")
}
}, []);
useEffect(() => {
fetchUsers();
}, [fetchUsers]);
return (
<Autocomplete
disablePortal
id="combo-box-demo"
options={getUsers.name} // should display dropdown options
renderInput={(params) => {
return <TextField {...params} />
}}
value={getUsers.id} // value for each dropdown item
/>
)
}
export default App;
I am getting the following error.
Warning: Failed prop type: The prop `options` is marked as required in `ForwardRef(Autocomplete)`, but its value is `undefined`.
Here is codesandbox link
You need to loop the options because you are retrieving an array, you can do that by using map
options={getUsers.map(el => el.name)}
import { useEffect, useCallback, useState } from "react";
import TextField from "#mui/material/TextField";
import Autocomplete from "#mui/material/Autocomplete";
function App() {
const [getUsers, setUsers] = useState([]);
const fetchUsers = useCallback(async () => {
try {
const response = await fetch(
"https://jsonplaceholder.typicode.com/users"
);
const userData = await response.json();
// console.log("userData: ", userData);
setUsers(userData);
} catch (error) {
console.log("Error in catch!");
}
}, []);
useEffect(() => {
fetchUsers();
}, [fetchUsers]);
return (
<Autocomplete
disablePortal
id="combo-box-demo"
options={getUsers.map(el => el.name)}
renderInput={(params) => {
return <TextField {...params} />;
}}
value={getUsers.id}
/>
);
}
I am currently trying to add a leaflet map to nextjs.
With predefined latitude and longitude in the component the display already works.
Now I want to display retrieved data from my api in the component as longitude and latitude.
This does not work because data.latitude is not set until my index page.
Do any of you have an idea how to get data.latitude and data.longitude from my index page into the component?
This is the code of Map component:
import React from "react";
import { TileLayer } from "react-leaflet";
import "leaflet/dist/leaflet.css";
import "leaflet-defaulticon-compatibility/dist/leaflet-defaulticon-compatibility.css";
import StyledMapContainer from "./styled.js";
import { Marker, Popup } from "react-leaflet";
import MarkerIcon from "../mapmarker/index.jsx";
import { data } from "../../pages/index";
console.log(data);
const Map = () => {
return (
<StyledMapContainer
watch
enableHighAccuracy
zoomControl
center={{ lat: data?.longitude, lng: data?.latitude }}
zoom={[13]}
scrollWheelZoom={false}
>
<TileLayer
url="https://api.mapbox.com/styles/v1/mapbox/streets-v11/tiles/256/{z}/{x}/{y}#2x?access_token="
zoomControl={true}
/>
<Marker position={{ lat: data?.longitude, lng: data?.latitude }} icon={MarkerIcon}>
<Popup>The Ship is here!</Popup>
</Marker>
</StyledMapContainer>
);
};
export default Map;
The code from the index is:
const Description = () => {
const { reload, query } = useRouter();
const { nr } = query;
const { name } = query;
const [data, setData] = useState();
const [unixTime, setunixTime] = useState();
const NoSsrMap = dynamic(() => import("../../atoms/map/index"), { ssr: false });
useEffect(async () => {
const { data } = await axios.get(`http://localhost:5000/${nr}`);
const extractedData = data.data;
setData(extractedData);
if (extractedData) {
const unixTimestamp = data.data.unixtime;
const millisecons = unixTimestamp * 1000;
const dateObj = new Date(millisecons);
const humanDateformat = dateObj.toLocaleString();
setunixTime(humanDateformat);
}
}, []);
const MyMap = () => {
return (
<div>
<NoSsrMap />
</div>
);
};
Try this
<StyledMapContainer
watch
enableHighAccuracy
zoomControl
center={{ lat: data?.longitude, lng: data?.latitude }}
zoom={[15]}
scrollWheelZoom={false}
>
I think a part of index.js is missing but I see some improvements for your code right now.
First of all, you shouldn't use useEffect like that in index.js, this is not how useEffect works to do an async function, you can try something like this
useEffect(() => {
const fetchData = async () => {
const { data } = await axios.get(`http://localhost:5000/${nr}`);
// ... do something with data like setData
};
fetchData();
}, []);
This is because the useEffect method should be a normal function.
After that, you could try passing data through props in the Map component, for example
const Map = ({data = {}}) => {
return (
<StyledMapContainer
watch
enableHighAccuracy
zoomControl
center={{ lat: data.longitude, lng: data.latitude }}
zoom={[13]}
scrollWheelZoom={false}
>
<TileLayer
url="https://api.mapbox.com/styles/v1/mapbox/streets-v11/tiles/256/{z}/{x}/{y}#2x?access_token="
zoomControl={true}
/>
<Marker position={{ lat: data.longitude, lng: data.latitude }} icon={MarkerIcon}>
<Popup>The Ship is here!</Popup>
</Marker>
</StyledMapContainer>
);
};
export default Map;
By doing this, your Map component does not depend on the page/index.
When trying to pass props in dynamic import try this
const NoSsrMap = dynamic(() => import("../../atoms/map/index"), { ssr: false });
const MyMap = ({data}) => {
return (
<div>
<NoSsrMap data={data} />
</div>
);
};
And finally use MyMap in your index page
<MyMap data={data}/>
With this your map component should works correctly.
I'd like to draw simple straight lines between multiple markers on Google Map. I render this map using google-map-react.
Map component
import { useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import GoogleMapReact from 'google-map-react';
import Marker from './marker';
export default function Map() {
const dispatch = useDispatch();
const markers = useSelector(state => state.points);
const [draggable, setDraggable] = useState(true);
const [center, setCenter] = useState({
lat: 59.95,
lng: 30.33
});
const [zoom, setZoom] = useState(14);
const moveMarker = (key, childProps, mouse) => {
let markersCopy = markers;
markersCopy.map(e => e.currPoint === key ? e.currCenter = mouse : e);
dispatch({type: 'REORDER', payload: markersCopy});
}
return (
<div style={{ height: '100vh', width: '100%' }}>
<GoogleMapReact
bootstrapURLKeys={{ key: process.env.REACT_APP_GOOGLE_MAPS_API_KEY }}
defaultCenter={center}
defaultZoom={zoom}
onChange={(e) => dispatch({type: 'CHANGE_CURR_CENTER', payload: e.center})}
draggable={draggable}
onChildMouseDown={() => setDraggable(false)}
onChildMouseUp={() => setDraggable(true)}
onChildMouseMove={(key, childProps, mouse) => moveMarker(key, childProps, mouse)}
>
{markers.map(e => {
return (
<Marker
lat={e.currCenter.lat}
lng={e.currCenter.lng}
text={e.currPoint}
key={e.currPoint}
/>
)
})}
</GoogleMapReact>
</div>
)
}
I'd like to have them connected by straight lines in the order in which they are
on the list (I'm adding them to the list manually in another component). The resulting line should represent the route, the first point in the list is the beginning of the route, the last one is the end of the route. How can I achieve this?
I'm getting geolocation from the user's browsers that work fine I can display the lon and lat on my screen but as soon as I try to push my data location to a fetch call in my useEffect, I not really what I did wrong...I get the error TypeError: Cannot read property 'photo' of undefined It seems that like the data is not being pushed to the fetch or the lon and lat may be empty in this case .can someone please kindly help
import React, { useEffect, useState } from 'react';
import './App.css';
import Map from './Componets/Map/Map';
import Location from './Componets/Api/Location';
import List from './Componets/List/List';
import Test from './Componets/test/Test';
require('dotenv').config();
const App = () => {
const [
Locations,
setLocations
] = useState([]);
const [
GoeLocationlng,
setGoeLocationlng
] = useState('');
const [
GoeLocationlat,
setGoeLocationlat
] = useState('');
const [
Imgdata,
setImgdata
] = useState([]);
const [
search,
setSearch
] = useState('');
const [
query,
setQuery
] = useState('bar');
useEffect(
() => {
async function example(lon, lat) {
let response1 = await fetch(
`https://www.flickr.com/services/rest/?method=flickr.photos.search&api_key=9c348b767f7a0403e907b0788188afba&text=${query}&tags=new&accuracy=+11&media=photos+&geo_context=1&per_page=20&page=1&lat=${parseFloat(
lat
)}&lon=${parseFloat(lon)}&radius=10&radius_units=km&format=json&nojsoncallback=1`
);
let json = await response1.json();
const promises = json.photos.photo.map((i) =>
fetch(
`https://www.flickr.com/services/rest/?method=flickr.photos.geo.getLocation&api_key=9c348b767f7a0403e907b0788188afba&photo_id=${i.id}&format=json&nojsoncallback=1`
)
);
const response2 = await Promise.all(promises);
const json2 = await Promise.all(response2.map((res) => res.json()));
return {
dataset1: json,
dataset2: json2
};
}
example(GoeLocationlng, GoeLocationlat)
.then((i) => {
setLocations(i.dataset1.photos.photo);
// console.log(i.dataset2.photo);
setImgdata(i.dataset2);
})
.catch((err) => {
console.log(err);
});
getLocation();
},
[
query
]
);
const updateSearch = (event) => {
setSearch(event.target.value);
};
const getSearch = (event) => {
event.preventDefault();
setQuery(search);
// setLocations({});
setSearch('');
};
const getLocation = () => {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(getCods);
} else {
alert('Geolocation is not supported by this browser.');
}
};
const getCods = (postion) => {
setGoeLocationlat(postion.coords.latitude);
setGoeLocationlng(postion.coords.longitude);
};
getLocation();
return (
<div className="App">
<Map
googleMapURL={`https://maps.googleapis.com/maps/api/js?key=AIzaSyA8i9z0T-J6oIs6Rrb7FUqz0rM1jipwrEg&v=3.exp&libraries=geometry,drawing,places`}
loadingElement={<div style={{ height: `100%` }} />}
containerElement={<div style={{ height: `400px` }} />}
mapElement={<div style={{ height: `100%` }} />}
Locations={{ Imgdata }}
/>
<form onSubmit={getSearch} className="searchForm">
<input className="search-bar" type="text" onChange={updateSearch} />
<button className="search-btn" type="submit" value="Search">
Search
</button>
</form>
{/* {Locations.map((location) => <Location key={location.id} title={location.title} />)} */}
{Locations.map((location) => (
<List
key={location.id}
title={location.title}
farm={location.farm}
server={location.server}
id={location.id}
secret={location.secret}
/>
))}
<h1>{GoeLocationlng}</h1>
</div>
);
};
export default App;
You should check your api again, it's not working correctly. It causes the error TypeError: Cannot read property 'photo' of undefined.