i'm using react google maps standalonesearchbox,every thing is ok,but how can i show first near by location in google map search hints(places),generally when we use map with search box then we attach both each other but here i didn't add map.
so here my question is how can i set center or show nearby search first on google places search hints.
here is my code
import React from 'react';
import {connect} from 'react-redux';
import { Input,Icon} from 'antd';
import 'antd/dist/antd.css';
import {pickupHandler,pickupAddHandler,dropoffHandler} from '../actions';
import config from '../../../config'
const { compose, withProps, lifecycle,withHandlers } = require("recompose");
const {
withScriptjs,
} = require("react-google-maps");
const { StandaloneSearchBox } = require("react-google-maps/lib/components/places/StandaloneSearchBox");
const SearchBox = compose(
withProps({
googleMapURL: config.MapApi,
loadingElement: <div style={{ height: `100%` }} />,
containerElement: <div style={{ height: `400px` }} />,
}),
lifecycle({
componentWillMount() {
const refs = {}
this.setState({
onSearchBoxMounted: ref => {
refs.searchBox = ref;
},
onBoundsChanged: () => {
this.setState({
bounds: refs.map.getBounds(),
center: refs.map.getCenter(),
})
},
onPlacesChanged: () => {
const places = refs.searchBox.getPlaces();
places.map(({ place_id, formatted_address, geometry: { location } }) =>{
this.props.latlngHandler({lat:location.lat(),lng:location.lng()})
this.props.AddressHandler(formatted_address)
})
this.setState({
places,
});
},
suffix: () =>{
this.props.AddressHandler('')
this.props.latlngHandler(false);
}
})
},
}),
withHandlers(() => {
return{
cutPickIcon:<Icon type="close-circle" />
}
}),
withScriptjs
)(props =>
<div data-standalone-searchbox="">
<StandaloneSearchBox
ref={props.onSearchBoxMounted}
bounds={props.bounds}
onBoundsChanged={props.onBoundsChanged}
onPlacesChanged={props.onPlacesChanged}
>
<Input
prefix={<Icon type="environment-o" style={props.name === 'pick' ? { color: '#EA4335' }: { color: '#00E64D' }} />}
type="text"
placeholder={props.placeHoler}
onChange={props.Field}
onFocus={props.FocusGA}
value={props.Address}
className='input'
suffix={props.Suffix ? <Icon type="close-circle" onClick={props.suffix}/> :''}
/>
</StandaloneSearchBox>
</div>
);
export default connect(null,{pickupAddHandler,pickupHandler,dropoffHandler})(SearchBox)
You can use the maps geocode to set bounds prop on StandaloneSearchBox.
Please refer to my answer on this post.
https://stackoverflow.com/a/53396781/1661712
Related
I am fetching list of testsuites using api call on component mount. Api returns list in chronological order.
Setting them as options for a select dropdown(Material-UI).
Then set the selected option to latest testSuite and using its Id get the corresponding testSuite data.
Data is retrieved successfully and pie chart is getting displayed.
Api calls are working fine and React dev tools shows the selectedTestSuite value to be set correctly.But DOM doesn't show the selection in the select dropdown.
Can someone please advise what is the mistake I am doing in this code? Thanks in advance.
import clsx from 'clsx';
import PropTypes from 'prop-types';
import { Doughnut } from 'react-chartjs-2';
import { makeStyles } from '#material-ui/styles';
import axios from 'axios';
import { useSpring, animated } from 'react-spring';
import '../../Dashboard.css';
import MenuItem from '#material-ui/core/MenuItem';
import {
Card,
CardHeader,
CardContent,
Divider,
TextField,
} from '#material-ui/core';
import CircularProgress from '#material-ui/core/CircularProgress';
const useStyles = makeStyles(() => ({
circularloader: {
position: 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
},
actions: {
justifyContent: 'flex-end',
},
inputField: {
width: '150px',
},
}));
const TestSuiteVsScanCount = (props) => {
const { className, ...rest } = props;
const classes = useStyles();
const [doughData, setDoughData] = useState([]);
const [dataLoadedFlag, setDataLoadedFlag] = useState(false);
const [testSuites, setTestSuites] = useState([]);
const [selectedTestSuite, setSelectedTestSuite] = useState({});
useEffect(() => {
function getTestSuites() {
axios.get('http://localhost:3000/api/v1/testsuite/12').then((resp) => {
setTestSuites(resp.data.reverse());
});
}
getTestSuites();
}, []);
useEffect(() => {
if (testSuites.length > 0) {
setSelectedTestSuite(() => {
return {
type: testSuites[0].TestSuiteName,
id: testSuites[0].TestSuiteId,
};
});
}
}, [testSuites]);
useEffect(() => {
function getTestSuiteData() {
let doughData = [];
if (selectedTestSuite.id) {
axios
.get(
'http://localhost:3000/api/v1/summary/piechart/12?days=30&testsuiteid=' +
selectedTestSuite.id,
)
.then((resp) => {
resp.data.forEach((test) => {
doughData = [test.TestCount, test.ScanCount];
});
setDoughData({
labels: ['Test Count', 'Scan Count'],
datasets: [
{
data: doughData,
backgroundColor: ['#FF6384', '#36A2EB'],
hoverBackgroundColor: ['#FF6384', '#36A2EB'],
},
],
});
setDataLoadedFlag(true);
});
}
}
getTestSuiteData();
}, [selectedTestSuite]);
const ChangeType = (id) => {
testSuites.forEach((suite) => {
if (suite.TestSuiteId === id) {
setSelectedTestSuite({
type: suite.TestSuiteName,
id: suite.TestSuiteId,
});
}
});
};
return (
<Card {...rest} className={clsx(classes.root, className)}>
<CardHeader
action={
<TextField
select
label="Select Test Suite"
placeholder="Select Tests"
value={selectedTestSuite.id}
className={classes.inputField}
name="tests"
onChange={(event) => ChangeType(event.target.value)}
variant="outlined"
InputLabelProps={{
shrink: true,
}}
>
{testSuites.map((testSuite) => (
<MenuItem
key={testSuite.TestSuiteId}
value={testSuite.TestSuiteId}
>
{testSuite.TestSuiteName}
</MenuItem>
))}
</TextField>
}
title="Test Suite vs Scan Count"
/>
<Divider />
<CardContent>
<div>
{dataLoadedFlag ? (
<Doughnut data={doughData} />
) : (
<CircularProgress
thickness="1.0"
size={100}
className={classes.circularloader}
/>
)}
</div>
</CardContent>
<Divider />
</Card>
);
};
TestSuiteVsScanCount.propTypes = {
className: PropTypes.string,
};
export default TestSuiteVsScanCount;
I was able to fix this issue with the help of my colleague by setting the initial state of selectedTestSuite to {type:'', id:0} instead of {}.
Changed this
const [selectedTestSuite, setSelectedTestSuite] = useState({});
To this
const [selectedTestSuite, setSelectedTestSuite] = useState({type:'', id:0});
But I am not sure why this worked.
I believe that the main problem is when you pass a value to TextField component with undefined, the TextField component will assume that is an uncontrolled component.
When you set you initial state for selectedTestSuite to be {} the value for selectedTestSuite.id will be undefined. You can find value API reference in https://material-ui.com/api/text-field/
I'm building out a Google Map on my Gatsby site with a search box that will allow users to search for their location. I've got a Hero component and a Map component, and all of the functionality is built into the Map component; the Google Maps API, autocomplete, Google Places etc. Here's what it looks like right now:
Map.tsx
import React, { useState, useRef, useCallback } from 'react';
import { GoogleMap, useLoadScript, Marker, InfoWindow } from '#react-google-maps/api';
import * as dealersData from 'assets/data/dealers.json';
import { Button } from 'components/button/Button';
import MapMarker from 'assets/images/icons/map-marker.png';
import SearchIcon from 'assets/svg/search.svg';
import usePlacesAutocomplete, { getGeocode, getLatLng } from 'use-places-autocomplete';
import {
Combobox,
ComboboxInput,
ComboboxPopover,
ComboboxList,
ComboboxOption,
} from '#reach/combobox';
import '#reach/combobox/styles.css';
import s from './Map.scss';
import MapStyles from './_MapStyles';
const libraries = ['places'];
const mapContainerStyle = {
width: '100%',
height: '100%',
};
const options = {
styles: MapStyles,
disableDefaultUI: true,
zoomControl: true,
};
interface MapProps {
location: any;
fetchedData: ReactNode;
}
export const Map = ({ location, fetchedData }: MapProps) => {
const center = {
lat: location.location.latitude,
lng: location.location.longitude,
};
const [selectedDealer, setselectedDealer] = useState(null);
const { isLoaded, loadError } = useLoadScript({
googleMapsApiKey: process.env.GATSBY_GOOGLE_MAPS_API,
libraries,
});
const mapRef = useRef();
const onMapLoad = useCallback((map) => {
mapRef.current = map;
}, []);
const panTo = useCallback(({ lat, lng }) => {
mapRef.current.panTo({ lat, lng });
mapRef.current.setZoom(10);
}, []);
if (loadError) return <div className={s.map}>Error loading maps...</div>;
if (!isLoaded) return <div className={s.map}>Loading...</div>;
return (
<>
<div className={s.map}>
<div className={s.map__search}>
<Search panTo={panTo}></Search>
</div>
<GoogleMap
mapContainerStyle={mapContainerStyle}
zoom={10}
center={center}
options={options}
onLoad={onMapLoad}
>
{dealersData.features.map((dealer) => (
<Marker
key={dealer.properties.DEALER_ID}
position={{
lat: dealer.geometry.coordinates[1],
lng: dealer.geometry.coordinates[0],
}}
onClick={() => {
setselectedDealer(dealer);
}}
icon={{
url: MapMarker,
scaledSize: new window.google.maps.Size(30, 30),
}}
/>
))}
{selectedDealer && (
<InfoWindow
position={{
lat: selectedDealer.geometry.coordinates[1],
lng: selectedDealer.geometry.coordinates[0],
}}
onCloseClick={() => {
setselectedDealer(null);
}}
>
<div className={s.dealer}>
<h2 className={s.dealer__name}>
{selectedDealer.properties.NAME}
<span className={s.phone}>{selectedDealer.properties.PHONE_NUMBER}</span>
</h2>
<div className={s.dealer__info}>
<p className={s.address}>{selectedDealer.properties.ADDRESS}</p>
<p className={s.address}>
{selectedDealer.properties.CITY}, {selectedDealer.properties.STATE}{' '}
{selectedDealer.properties.ZIP_CODE}
</p>
</div>
<Button>Set Location</Button>
</div>
</InfoWindow>
)}
</GoogleMap>
</div>
</>
);
};
export function Search({ panTo }) {
const {
ready,
value,
suggestions: { status, data },
setValue,
clearSuggestions,
} = usePlacesAutocomplete({
requestOptions: {
location: { lat: () => 38.8299359, lng: () => -121.3070356 },
radius: 200 * 1000,
},
});
return (
<>
<div className={s.search__input}>
<i className={s.search__icon}>
<SearchIcon />
</i>
<Combobox
onSelect={async (address) => {
setValue(address, false);
clearSuggestions();
try {
const results = await getGeocode({ address });
const { lat, lng } = await getLatLng(results[0]);
panTo({ lat, lng });
} catch (error) {
console.log('error');
}
}}
>
<ComboboxInput
value={value}
onChange={(e: any) => {
setValue(e.target.value);
}}
disabled={!ready}
placeholder="Enter your location"
/>
<ComboboxPopover>
<ComboboxList>
{status === 'OK' &&
data.map(({ id, description }) => <ComboboxOption key={id} value={description} />)}
</ComboboxList>
</ComboboxPopover>
</Combobox>
</div>
</>
);
}
And here's my Hero component:
Hero.tsx
import React from 'react';
import s from './Hero.scss';
import { RichText } from 'prismic-reactjs';
import { linkResolver } from 'utils/linkResolver';
import htmlSerializer from 'utils/htmlSerializer';
import { Search } from '../map/_Map';
export const Hero = ({ content, panTo }: any) => (
<div className={s.hero} data-theme={content.theme}>
<div className={s.hero__container}>
<div className={s.content}>
<h1 className={s.content__heading}>{RichText.asText(content.page_title)}</h1>
<div className={s.content__copy}>
{RichText.render(content.copy, linkResolver, htmlSerializer)}
</div>
<Search panTo={panTo}></Search>
</div>
</div>
</div>
);
Essentially, I'm needing to utilize the Search function in my Hero component, but when I export it and import it into the Hero its rendering just fine, but it never loads the use-places-autocomplete library, and won't work. What am I doing wrong? Is there any way to export the search function to reuse?
I've created an SSCCE, as asked in the comments here. The search function works if I utilize it directly in the Map component, but if it's imported into the hero, it doesn't load.
https://codesandbox.io/s/dawn-glitter-zi7lx
Thanks.
Thanks for providing the sscce of your code in codesandbox. It looks like you are loading the Google Maps Javascript script tag only inside the <Map/> and not inside <Hero/>. You can see in your console log that there is a message to load the library.
To make it work, I used LoadScript of #react-google-maps/api in your index.tsx and put both and components in order for both script to work. Here's a sample of the index.tsx.
import React from "react";
import ReactDOM from "react-dom";
import { LoadScript } from "#react-google-maps/api";
import { Map } from "./Map";
import { Hero } from "./Hero";
const rootElement = document.getElementById("root");
const App = () => {
return (
<React.StrictMode>
<LoadScript
googleMapsApiKey="YOUR_API_KEY"
libraries={["places"]}
>
<Hero />
<Map />
</LoadScript>
</React.StrictMode>
);
};
ReactDOM.render(<App />, rootElement);
I also removed useLoadScript in Map.tsx to make sure that it won't have conflict with the script loaded in the index.tsx.
I'm trying to follow tutorials make bounds and center the map based on all the marks when the map is first rendered. It works on the map page, however I got this error when I use back button to go to other pages. "index.js:1446 Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method."
May I know how to solve this? Thank you so much!
import React, { Component } from 'react';
import {
withGoogleMap,
GoogleMap,
withScriptjs,
Marker,
InfoWindow
} from "react-google-maps";
import { compose, withProps, withStateHandlers, lifecycle } from "recompose";
import Constants from '../Constants';
import MapMarker from './MapMarker';
const CardTransactionMapRGMs = compose(
withProps({
googleMapURL:
`https://maps.googleapis.com/maps/api/js?key=${Constants.GOOGLE_MAP_API_KEY}&libraries=geometry,drawing,places`,
loadingElement: <div style={{ height: `100%` }} />,
containerElement: <div style={{ height: "70vh", width: "100%" }} />,
mapElement: <div style={{ height: "100%" }} />
}),
withStateHandlers(
props => ({
infoWindows: props.geo.map(p => {
return { isOpen: false };
})
}),
{
onToggleOpen: ({ infoWindows }) => selectedIndex => ({
infoWindows: infoWindows.map((iw, i) => {
iw.isOpen = selectedIndex === i;
return iw;
})
})
}
),
lifecycle({
componentDidMount() {
this.setState({
zoomToMarkers: map => {
//console.log("Zoom to markers");
const bounds = new window.google.maps.LatLngBounds();
map.props.children.forEach((child) => {
if (child.type === Marker) {
bounds.extend(new window.google.maps.LatLng(child.props.position.lat, child.props.position.lng));
}
})
map.fitBounds(bounds);
}
})
},
}),
withScriptjs,
withGoogleMap
)(props => (
<GoogleMap ref={props.zoomToMarkers} defaultZoom={props.zoom} defaultCenter={props.center}>
{
props.geo &&
props.geo.map((geo, i) => {
return (
<Marker
id={geo.id}
key={geo.id}
position={{ lat: geo.lat, lng: geo.lng }}
title="Click to zoom"
onClick={props.onToggleOpen.bind(this, i)}
>
{props.infoWindows[i].isOpen && (
<InfoWindow onCloseClick={props.onToggleOpen.bind(i)}>
<div>{geo.amount} </div>
</InfoWindow>
)}
</Marker>
);
})
}
</GoogleMap >
));
export default CardTransactionMapRGMs;
i'm new in react js and i'm trying to fetch data from My API , which i can its result with POSTMAN , and it shows the data
My problem is when i use the link :" http://localhost:51492/api/user/1 " in my react js app , data couldn't appear ...
PS : je travail avec Code SandBox
here is my code showing all the followers of a user :
import React from "react";
import ReactDOM from "react-dom";
import "antd/dist/antd.css";
import { List, Avatar, Button, Spin } from "antd";
import PropTypes from "prop-types";
import { withStyles } from "#material-ui/core/styles";
import reqwest from "reqwest";
const fakeDataUrl =
"http://localhost:51492/api/follower/all/1";
class LoadMoreList extends React.Component {
state = {
loading: true,
loadingMore: false,
showLoadingMore: true,
data: []
};
componentDidMount() {
this.getData(res => {
this.setState({
loading: false,
data: res.results
});
});
}
getData = callback => {
reqwest({
url: fakeDataUrl,
type: "json",
method: "get",
contentType: "application/json",
success: res => {
callback(res);
}
});
};
onLoadMore = () => {
this.setState({
loadingMore: true
});
this.getData(res => {
const data = this.state.data.concat(res.results);
this.setState(
{
data,
loadingMore: false
},
() => {
// Resetting window's offsetTop so as to display react-virtualized demo underfloor.
// In real scene, you can using public method of react-virtualized:
// https://stackoverflow.com/questions/46700726/how-to-use-public-method-updateposition-of-react-virtualized
window.dispatchEvent(new Event("resize"));
}
);
});
};
render() {
const { loading, loadingMore, showLoadingMore, data } = this.state;
const loadMore = showLoadingMore ? (
<div
style={{
textAlign: "center",
marginTop: 12,
height: 32,
lineHeight: "32px"
}}
>
{loadingMore && <Spin />}
{!loadingMore && (
<Button onClick={this.onLoadMore}>loading more</Button>
)}
</div>
) : null;
return (
<List
style={{
width: "50%",
left: "25%"
}}
className="demo-loadmore-list"
loading={loading}
itemLayout="horizontal"
loadMore={loadMore}
dataSource={data}
renderItem={item => (
<List.Item
actions={[
<Button type="primary" icon="user-add">
suivre
</Button>,
<a>Message</a>
]}
>
<List.Item.Meta
avatar={
<a>
<Avatar src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png" />{" "}
</a>
}
title={{item.userProfile}}
/>
</List.Item>
)}
/>
);
}
}
LoadMoreList.propTypes = {
classes: PropTypes.object.isRequired
};
export default withStyles()(LoadMoreList);
and here is what PostMan shows when i enter the URL : http://localhost:51492/api/follower/all/1
what i thinks is missing is the "results attribute" at the beginning of the result in postman , i think it must be like that :
please help me , and thank u for ur interest
I am using example in react-google-maps library.
const { compose, withProps, lifecycle } = require("recompose");
const {
withScriptjs,
withGoogleMap,
GoogleMap,
DirectionsRenderer,
} = require("react-google-maps");
const MapWithADirectionsRenderer = compose(
withProps({
googleMapURL: "https://maps.googleapis.com/maps/api/js?key=AIzaSyC4R6AN7SmujjPUIGKdyao2Kqitzr1kiRg&v=3.exp&libraries=geometry,drawing,places",
loadingElement: <div style={{ height: `100%` }} />,
containerElement: <div style={{ height: `400px` }} />,
mapElement: <div style={{ height: `100%` }} />,
}),
withScriptjs,
withGoogleMap,
lifecycle({
componentDidMount() {
const DirectionsService = new google.maps.DirectionsService();
DirectionsService.route({
origin: new google.maps.LatLng(41.8507300, -87.6512600),
destination: new google.maps.LatLng(41.8525800, -87.6514100),
travelMode: google.maps.TravelMode.DRIVING,
}, (result, status) => {
if (status === google.maps.DirectionsStatus.OK) {
this.setState({
directions: result,
});
} else {
console.error(`error fetching directions ${result}`);
}
});
}
})
)(props =>
<GoogleMap
defaultZoom={7}
defaultCenter={new google.maps.LatLng(41.8507300, -87.6512600)}
>
{props.directions && <DirectionsRenderer directions={props.directions} />}
</GoogleMap>
);
<MapWithADirectionsRenderer />
I want to enable alternative routes in my map. So I used
provideRouteAlternatives: true
so inside callback function
(result, status) => { }
the result have a property routes which is an array of alternative routes.
How can I render those routes into map ? .. I also want to click on routes and they will change the color from active to inactive. When user select the route I need to send on the server property called
overview_polyline
which is inside of routes array, where each route inside the array has this property.
Thank you very much.
If you only want to render those routes on the map, you could use DirectionsRenderer from that library.
https://tomchentw.github.io/react-google-maps/#directionsrenderer
However, this DirectionsRenderer component can not be fully customized like defining the colors or onClick functions. What you could do is creating a customized Directions component using Marker and Polygon which also come from this library. Below is how I made it:
import React, { Component } from 'react';
import { Polyline, Marker } from 'react-google-maps';
import { pinkA200, blue500 } from 'material-ui/styles/colors';
import ntol from 'number-to-letter';
import _ from 'lodash';
const DirectionMarker = ({ data, isEnd, i, onClick }) => {
const { start_location, end_location } = data;
if (isEnd) {
return [
<Marker onClick={onClick} position={start_location} label={ntol(i)} key="end0" />,
<Marker onClick={onClick} position={end_location} label={ntol(i + 1)} key="end1" />
];
}
return <Marker onClick={onClick} position={start_location} label={ntol(i)} />;
};
const Direction = ({ direction, isEnd, i, onClick, isSelected }) => {
const data = direction.routes[0].legs[0];
const path = data.steps.reduce((sum, current) => _.concat(sum, current.path), []);
return [
<DirectionMarker data={data} onClick={onClick} isEnd={isEnd} i={i} key="marker" />,
<Polyline
onClick={onClick}
path={path}
options={{
strokeColor: isSelected ? pinkA200 : blue500,
strokeOpacity: 0.6,
strokeWeight: 6
}}
key="line"
/>
];
};
class Directions extends Component {
constructor(props) {
super(props);
this.state = { selectedSegment: 0 };
}
render() {
const { directions } = this.props;
if (_.isEmpty(directions)) {
return false;
}
return directions.map((d, i) => {
const directionProps = {
direction: d,
i,
key: i,
onClick: () => this.setState({ selectedSegment: i }),
isEnd: i === directions.length - 1,
isSelected: i === this.state.selectedSegment
};
return <Direction {...directionProps} />;
});
}
}
export default Directions;