Menu items are not clickable after rendering Google Maps container - reactjs

My semantic-ui-react Menu component is acting strange when I add my google maps component to be rendered. Before I added my MapContainer, the FilterSection was working pretty fine. But after I render the MapContainer my sections in my Menu are no longer clickable nor do they react to my mouse hover. What should I do?
Snippet of render:
return (
<div>
<Navbar/>
<FilterSection/>
<ChatWidget/>
<div className="map-and-cards">
<MapContainer/> // This line causes the problem
<div style={containerStyle} className="cards-container">
<div class="card-columns" style={{gridTemplateColumns: '350px 350px', display: 'grid', rowGap: '50px', columnGap: '18px'}}>
<Cards listingData={listingData}/>
</div>
</div>
</div>
</div>
)
and my MapContainer.js:
import React from 'react';
import { Map, GoogleApiWrapper } from 'google-maps-react';
const style = {
position: 'relative',
top: '65px',
width: '47.5%', // 47.5
height: '85%',
};
const defaultCenter = {
lat: 41.04137, lng: 28.979530
}
export class MapContainer extends React.Component<MapProps> {
render() {
return (
<Map
style = {style}
google={this.props.google}
centerAroundCurrentLocation={true}
zoom={12}
initialCenter={defaultCenter}
/>
);
}
}
export default GoogleApiWrapper({
apiKey: (0)
})(MapContainer)
FilterSection.js:
import React, { Component } from "react";
import { Menu, Dropdown, Input, Button } from "semantic-ui-react";
import 'semantic-ui-css/semantic.min.css'
export class Price extends Component {
state = { isOpen: false };
handleItemClick = (e, { name }) => this.setState({ activeItem: name });
render() {
const { activeItem } = this.state;
return (
<div style={{float: 'left'}}>
<Menu secondary >
<Menu.Item>
<Dropdown
text={"Price"}
className="item"
onClick={this.toggleOpen}
open={this.state.isOpen}
>
<Dropdown.Menu onClick={this.preventClosing}>
<Dropdown.Header icon="money" content={" Price Range"} />
<Dropdown.Divider />
<form
style = {{margin: '10px'}}
name="id"
type="number"
max={900}
action={<Button onClick={this.toggleOpen} content={"Send"} />}
>
<Input style={{zoom: '0.9'}} placeholder="min"/> — <Input style={{zoom: '0.9'}} placeholder="max"/>
</form>
</Dropdown.Menu>
</Dropdown>
</Menu.Item>
</Menu>
</div>
);
}
toggleOpen = () => this.setState({ isOpen: !this.state.isOpen });
preventClosing = e => e.stopPropagation();
}
export class BedsAndBaths extends Component {
state = { isOpen: false };
handleItemClick = (e, { name }) => this.setState({ activeItem: name });
render() {
const { activeItem } = this.state;
return (
<div style={{float: 'left'}}>
<Menu secondary>
<Menu.Item>
<Dropdown
text={"Bedrooms & Livingrooms"}
className="item"
onClick={this.toggleOpen}
open={this.state.isOpen}
>
<Dropdown.Menu onClick={this.preventClosing}>
<form
style = {{margin: '10px'}}
name="id"
type="number"
max={900}
action={<Button onClick={this.toggleOpen} content={"Send"} />}
>
<Input style={{zoom: '0.9'}} placeholder="3"/> + <Input style={{zoom: '0.9'}} placeholder="1"/>
</form>
</Dropdown.Menu>
</Dropdown>
</Menu.Item>
</Menu>
</div>
);
}
toggleOpen = () => this.setState({ isOpen: !this.state.isOpen });
preventClosing = e => e.stopPropagation();
}
export class More extends Component {
state = { isOpen: false };
handleItemClick = (e, { name }) => this.setState({ activeItem: name });
render() {
const { activeItem } = this.state;
return (
<div style={{float: 'left'}}>
<Menu secondary>
<Menu.Item>
<Dropdown
text={"More"}
className="item"
onClick={this.toggleOpen}
open={this.state.isOpen}
>
<Dropdown.Menu onClick={this.preventClosing}>
<Dropdown.Header icon="money" content={" Price Range"} />
<Dropdown.Divider />
<form
style = {{margin: '10px'}}
name="id"
type="number"
max={900}
action={<Button onClick={this.toggleOpen} content={"Send"} />}
>
<Input style={{zoom: '0.9'}} placeholder="min"/> — <Input style={{zoom: '0.9'}} placeholder="max"/>
</form>
</Dropdown.Menu>
</Dropdown>
</Menu.Item>
</Menu>
</div>
);
}
toggleOpen = () => this.setState({ isOpen: !this.state.isOpen });
preventClosing = e => e.stopPropagation();
}
export class Alert extends Component {
state = { isOpen: false };
handleItemClick = (e, { name }) => this.setState({ activeItem: name });
render() {
const { activeItem } = this.state;
return (
<div style={{float: 'left', position: 'relative', top: '11.5px', left: '15px', zoom: '0.9'}}>
<Menu secondary >
<button class="ui button ">
Create Alert
</button>
</Menu>
</div>
);
}
toggleOpen = () => this.setState({ isOpen: !this.state.isOpen });
preventClosing = e => e.stopPropagation();
}
export class Search extends Component {
state = { isOpen: false };
handleItemClick = (e, { name }) => this.setState({ activeItem: name });
render() {
const { activeItem } = this.state;
return (
<div style={{float: 'left', position: 'relative', top: '12px', right: '50px', zoom: '0.9'}}>
<Menu secondary >
<div style={{float: 'left', position: 'relative', right: '10px'}} class="ui input focus">
<input type="text" placeholder="Enter an adress, neigborhood, city, or ZIP code
" size="45"/>
</div>
<button class="ui button ">
Search
</button>
</Menu>
</div>
);
}
toggleOpen = () => this.setState({ isOpen: !this.state.isOpen });
preventClosing = e => e.stopPropagation();
}
export default function FilterSection(){
return (
<div style={{position: 'relative', left: '75px', bottom: '1px'}}><Search/><div style={{position: 'relative', right: '30px'}}><More/><BedsAndBaths/><Price/><Alert/></div></div>
)
}
As I said, only the MapContainer component is triggering the deactivation of the menu.
You can see the actual page at royaremlak.com/search

Related

Passing value of component in separate jsx file's value to event on Full-Calendar

I am new to React. I am trying to pass the value of a select box imported from a separate .jsx file to an event for full-calendar. I can't find anything on this or figure out how to do it for the life of me. This is my component for the select box in a separate file.
import React from 'react'
export const Dropdown = (props) => (
<div className="form-group">
<select
className="form-control"
name="partner"
onChange={props.onChange}
>
<option defaultValue>Select Partner</option>
{props.options.map((item, index) => (
<option key={index} value={item.value}>
{item.label}
</option>
))}
</select>
</div>
)
export default class DropdownList extends React.Component {
constructor() {
super()
this.state = {
list: [],
chosenValue: '',
}
}
componentDidMount() {
fetch('/calendar/users')
.then((response) => response.json())
.then((item) => this.setState({ list: item }))
}
onDropdownChange = (e) => {
this.setState({ chosenValue: e.target.value })
}
render() {
return (
<div className="form-group">
<Dropdown
name="partner"
id="partner"
options={this.state.list}
onDropdownChange={this.onDropdownChange}
/>
</div>
)
}
}
This is my modal that contains the form to pass through as an event to full-calendar
import React, {useState} from "react";
import Modal from "react-modal";
import DateTimePicker from "react-datetime-picker";
import 'bootstrap-icons/font/bootstrap-icons.css'
import DropdownList from "./SelectBox";
export default function ({ isOpen, onClose, onEventAdded }) {
const [title, setTitle] = useState("");
const [start, setStart] = useState(new Date());
const [end, setEnd] = useState(new Date());
const [selectValue] = useState(DropdownList);
const onSubmit = (event) => {
event.preventDefault();
onEventAdded({
title,
start,
partner: selectValue,
end
});
onClose()
};
return (
<Modal isOpen={isOpen}
onClose={onClose}
style={{
overlay: {
backgroundColor: 'rgba(0, 0, 0, 0.5)'
},
content: {
top: '50%',
left: '50%',
right: 'auto',
bottom: 'auto',
marginRight: '-50%',
transform: 'translate(-50%, -50%)',
border: '1px solid #ccc',
background: '#fff',
overflow: 'auto',
WebkitOverflowScrolling: 'touch',
borderRadius: '4px',
outline: 'none',
padding: '20px',
maxWidth: '1200px',
minHeight: '80%',
textAlign: 'center'
}
}}
>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootswatch#4.5.2/dist/lux/bootstrap.min.css" />
<form onSubmit={onSubmit}>
<div className='form-group'>
<input className='form-control' placeholder="Title" value={title} onChange={e => setTitle(e.target.value)} />
</div>
<DropdownList />
<div className='form-group'>
<DateTimePicker name='start' value={start} onChange={date => setStart(date)} />
</div>
<div className='form-group'>
<DateTimePicker name='end' value={end} onChange={date =>setEnd(date)} />
</div>
<button className="btn btn-primary btn-block" type="submit">Add Event</button>
<button className="btn btn-danger btn-block" onClick={onClose}>Cancel</button>
</form>
</Modal>
)
}
I have tried DropdownList.value, redoing the onDropdownChange(e) function in the modal jsx file to no avail. I'm sure this is really simple and I'm getting a headache so thank you for your kindness ahead of time. I'm currently trying to use props but it's all coming up undefined.
Edit: tried to add to component in the modal file
const [partner, setPartner] = useState();
<DropdownList
ref={partnerRef}
onDropdownChange={e => setPartner(this.chosenValue = e.target.value)}
/>

using a value through react components

I'm new to React and been struggling with new challenges. Please write me your opinion and help me to get out of this challenge. I need to take the input value and append it in list area. Basically getting that state.inputValue and append it in ul of listArea.
This is InputTask component.
import React from 'react';
class InputTask extends React.Component {
state = { inputValue: ''}
onInputSubmit = (e) => {
this.setState({inputValue: e.target.value})
}
handleSubmit = (e) => {
e.preventDefault();
console.log('A name was submitted: ' + this.state.inputValue);
}
render() {
return (
<form className="ui input" onSubmit={this.handleSubmit} style={{marginRight:10}}>
<input type="text" value={ this.state.inputValue } onChange={this.onInputSubmit}/>
</form>
)
}
}
export default InputTask;
And this is listArea component.
import React from 'react';
class ListArea extends React.Component{
render() {
const style={
backgroundColor: "#f5d556",
height: "400px",
padding: "40px",
borderRadius: "20px"
};
const listStyle = {
backgroundColor: "green",
color: "white",
listStyle: "none",
textAlign: "left",
padding: "5px",
marginBottom: "10px",
borderRadius: "5px"
}
const iconStyle = {
float: "right"
}
const labelStyle = {
color: "white"
}
return(
<div className="ui form" style={{marginBottom:10}}>
<div className="field">
<div style={style}>
<ul style={{paddingLeft: 0}}>
<li style={listStyle}>
<div className="ui checkbox">
<input type="checkbox" name="example"/>
<label style={labelStyle}> hello there!</label>
</div>
<i className="white trash icon" style={iconStyle}></i>
</li>
</ul>
</div>
</div>
</div>
)
}
}
export default ListArea;
this is App component
import React from 'react';
import ReactDOM from 'react-dom';
import InputTask from "./components/inputTask";
import ListArea from "./components/listArea";
class App extends React.Component {
render() {
return (
<div className="ui container" style={{textAlign:'center'}}>
<h1>Tasks</h1>
<ListArea />
<InputTask />
</div>
);
}
}
ReactDOM.render(<App />,document.getElementById('root'));
In InputTask component at handleSubmit with this.props.InputSelected(this.state.inputValue);
I sent back selected input to it's parent(App.js)
import React from "react";
class InputTask extends React.Component {
state = { inputValue: "" };
onInputSubmit = (e) => {
this.setState({ inputValue: e.target.value });
};
handleSubmit = (e) => {
e.preventDefault();
console.log("A name was submitted: " + this.state.inputValue);
this.props.InputSelected(this.state.inputValue);
this.setState({ inputValue: "" });
};
render() {
return (
<form
className="ui input"
onSubmit={this.handleSubmit}
style={{ marginRight: 10 }}
>
<input
type="text"
value={this.state.inputValue}
onChange={this.onInputSubmit}
/>
</form>
);
}
}
export default InputTask;
In App component I got selected value with handleInputSelected and added it to list to send to ListArea component
import React from "react";
import InputTask from "./components/inputTask";
import ListArea from "./components/listArea";
class App extends React.Component {
state = { list: [] };
handleInputSelected = (input) => {
const { list } = { ...this.state };
list.push(input);
this.setState(list);
};
render() {
return (
<div className="ui container" style={{ textAlign: "center" }}>
<h1>Tasks</h1>
<ListArea list={this.state.list} />
<InputTask InputSelected={(input) => this.handleInputSelected(input)} />
</div>
);
}
}
export default App;
And finally in ListArea component with map on list I just append li element to ul.
import React from "react";
class ListArea extends React.Component {
render() {
console.log("data: ", this.props.data);
const style = {
backgroundColor: "#f5d556",
height: "400px",
padding: "40px",
borderRadius: "20px",
overflow: "scroll"
};
const listStyle = {
backgroundColor: "green",
color: "white",
listStyle: "none",
textAlign: "left",
padding: "5px",
marginBottom: "10px",
borderRadius: "5px"
};
const iconStyle = {
float: "right"
};
const labelStyle = {
color: "white"
};
console.log(this.props.data);
return (
<div className="ui form" style={{ marginBottom: 10 }}>
<div className="field">
<div style={style}>
<ul style={{ paddingLeft: 0 }}>
<li style={listStyle}>
<div className="ui checkbox">
<input type="checkbox" name="example" />
<label style={labelStyle}> hello there!</label>
</div>
<i className="white trash icon" style={iconStyle}></i>
</li>
{this.props.list.map((m) => (
<li style={listStyle}>
<div className="ui checkbox">
<input type="checkbox" name="example" />
<label style={labelStyle}> {m}</label>
</div>
<i className="white trash icon" style={iconStyle}></i>
</li>
))}
</ul>
</div>
</div>
</div>
);
}
}
export default ListArea;

Update the input value from one component in an other on runtime

I have a NotificationStepper Component which gets inputs from user, I need to update this input value in run time in another component named TabComponent. Both of the components are child components of SendNotification Component. How can i achieve that in reactjs.
EDIT
NotificationStepper.js
const styles = {
transparentBar: {
backgroundColor: 'transparent !important',
boxShadow: 'none',
paddingTop: '25px',
color: '#FFFFFF'
}
};
const useStyles = makeStyles((theme: Theme) =>
createStyles({
formControl: {
margin: theme.spacing(1),
minWidth: 120,
},
selectEmpty: {
marginTop: theme.spacing(2),
},
}),
);
function getSteps() {
return ['Create', 'Audience', 'Timing'];
}
function getStepContent(step, $this) {
switch (step) {
case 0:
return (
<div className="row">
<CardBox styleName="col-lg-12"
heading="">
<form className="row" noValidate autoComplete="off" style={{"flex-wrap":"no-wrap", "flex-direction": "column" }}>
<div className="col-md-12 col-12">
<TextField
id="campaign_name"
label="Campaign Name"
value={$this.state.name}
onChange={$this.handleChange('name')}
margin="normal"
fullWidth
/>
</div>
</form>
</CardBox>
</div>
);
default:
return 'Unknown step';
}
}
class NotificationStepper extends React.Component {
state = {
activeStep: 0,
name: '',
};
handleChange = name => event => {
this.setState({
[name]: event.target.value,
});
};
handleNext = () => {
this.setState({
activeStep: this.state.activeStep + 1,
});
};
handleBack = () => {
this.setState({
activeStep: this.state.activeStep - 1,
});
};
handleReset = () => {
this.setState({
activeStep: 0,
});
};
render() {
const steps = getSteps();
const {activeStep} = this.state;
return (
<div className="col-xl-12 col-lg-12 col-md-7 col-12">
<Stepper className="MuiPaper-root-custom" activeStep={activeStep} orientation="vertical">
{steps.map((label, index) => {
return (
<Step key={label}>
<StepLabel>{label}</StepLabel>
<StepContent className="pb-3">
<Typography>{getStepContent(index, this)}</Typography>
<div className="mt-2">
<div>
<Button
disabled={activeStep === 0}
onClick={this.handleBack}
className="jr-btn"
>
Back
</Button>
<Button
variant="contained"
color="primary"
onClick={this.handleNext}
className="jr-btn"
>
{activeStep === steps.length - 1 ? 'Finish' : 'Next'}
</Button>
</div>
</div>
</StepContent>
</Step>
);
})}
</Stepper>
{activeStep === steps.length && (
<Paper square elevation={0} className="p-2">
<Typography>All steps completed - you"re finished</Typography>
<Button onClick={this.handleReset} className="jr-btn">
Reset
</Button>
</Paper>
)}
</div>
);
}
}
export default NotificationStepper;
TabComponent.js
TabContainer.propTypes = {
children: PropTypes.node.isRequired,
dir: PropTypes.string.isRequired,
};
class TabComponent extends Component {
state = {
value: 0,
};
render() {
const {theme} = this.props;
return (
<div className="col-xl-12 col-lg-12 col-md-12 col-12" style={{"margin-top": "15px"}}>
<NotifCard key={0} data={{'name': 'Title of Notification', 'company': 'sayge.ai', 'image': require("assets/images/chrome.png"), 'description': 'Notification Message here'}} styleName="card shadow "/>
</div>
);
}
}
TabComponent.propTypes = {
theme: PropTypes.object.isRequired,
};
export default withStyles(null, {withTheme: true})(TabComponent);
and this is my SendNotification.js
const SendNotification = ({match}) => {
return (
<div className="dashboard animated slideInUpTiny animation-duration-3">
<ContainerHeader match={match} title={<IntlMessages id="sidebar.notification"/>}/>
<div className="row" style={{'flex-wrap': 'no wrap', "flex-direction": 'row'}}>
<div className="col-xl-7 col-lg-7 col-md-7 col-7">
<NotificationStepper/>
<div className='flex-class' style={{'width': '100%'}}>
<Button color="primary" style={{"align-self": "flex-end", "border" : "1px solid", "margin-left": "10px", "margin-bottom": "40px"}} size="small" className="col-md-2 col-2">Fetch</Button>
<Button color="primary" style={{"align-self": "flex-end", "border" : "1px solid", "margin-left": "10px", "margin-bottom": "40px"}} size="small" className="col-md-2 col-2" color="primary">Discard</Button>
</div>
</div>
<div className="col-xl-5 col-lg-5 col-md-5 col-5" style={{"padding-top": "20px"}}>
<span style={{"margin-left" : "20px", "font-weight": "bold"}}>Preview</span>
<TabComponent />
</div>
</div>
</div>
);
};
export default SendNotification;
i need to get the name value from NotificationStepper and Update it in pass it to in TabComponent.
There are plenty of tutorials about that. For instance, this.
import React, { Component, createRef } from "react";
class CustomTextInput extends Component {
textInput = createRef();
focusTextInput = () => this.textInput.current.focus();
render() {
return (
<>
<input type="text" ref={this.textInput} />
<button onClick={this.focusTextInput}>Focus the text input</button>
</>
);
}
}
import React, { useRef } from "react";
const CustomTextInput = () => {
const textInput = useRef();
focusTextInput = () => textInput.current.focus();
return (
<>
<input type="text" ref={textInput} />
<button onClick={focusTextInput}>Focus the text input</button>
</>
);
}

Pass props to child component from parent component dynamically

I have a child component StepperNotification which gets an input from user and returns it as prop to its child component in following way.
const styles = {
transparentBar: {
backgroundColor: 'transparent !important',
boxShadow: 'none',
paddingTop: '25px',
color: '#FFFFFF'
}
};
const useStyles = makeStyles((theme: Theme) =>
createStyles({
formControl: {
margin: theme.spacing(1),
minWidth: 120,
},
selectEmpty: {
marginTop: theme.spacing(2),
},
}),
);
function getSteps() {
return ['Create', 'Audience', 'Timing'];
}
function getStepContent(step, $this) {
switch (step) {
case 0:
return (
<div className="row">
<CardBox styleName="col-lg-12"
heading="">
<form className="row" noValidate autoComplete="off" style={{"flex-wrap":"no-wrap", "flex-direction": "column" }}>
<div className="col-md-12 col-12">
<TextField
id="campaign_name"
label="Campaign Name"
value={$this.state.name}
onChange={$this.handleChange('name')}
margin="normal"
fullWidth
/>
</div>
</form>
</CardBox>
</div>
);
default:
return 'Unknown step';
}
}
class NotificationStepper extends React.Component {
state = {
activeStep: 0,
name: '',
};
handleChange = name => event => {
this.setState({
[name]: event.target.value,
});
this.props.titlechange(event.target.value);
};
handleNext = () => {
this.setState({
activeStep: this.state.activeStep + 1,
});
};
handleBack = () => {
this.setState({
activeStep: this.state.activeStep - 1,
});
};
handleReset = () => {
this.setState({
activeStep: 0,
});
};
render() {
const steps = getSteps();
const {activeStep} = this.state;
return (
<div className="col-xl-12 col-lg-12 col-md-7 col-12">
<Stepper className="MuiPaper-root-custom" activeStep={activeStep} orientation="vertical">
{steps.map((label, index) => {
return (
<Step key={label}>
<StepLabel>{label}</StepLabel>
<StepContent className="pb-3">
<Typography>{getStepContent(index, this)}</Typography>
<div className="mt-2">
<div>
<Button
disabled={activeStep === 0}
onClick={this.handleBack}
className="jr-btn"
>
Back
</Button>
<Button
variant="contained"
color="primary"
onClick={this.handleNext}
className="jr-btn"
>
{activeStep === steps.length - 1 ? 'Finish' : 'Next'}
</Button>
</div>
</div>
</StepContent>
</Step>
);
})}
</Stepper>
{activeStep === steps.length && (
<Paper square elevation={0} className="p-2">
<Typography>All steps completed - you"re finished</Typography>
<Button onClick={this.handleReset} className="jr-btn">
Reset
</Button>
</Paper>
)}
</div>
);
}
}
export default NotificationStepper;
In my parent component i am getting this prop value and passing it to another child component Tabcomponent in following way
ParentCompoennt.js
const SendNotification = ({match}) => {
let titlename = '';
let message = '';
function handleTitle(title_) {
console.log('in here');
console.log(title_);
titlename = title_;
}
return (
<div className="dashboard animated slideInUpTiny animation-duration-3">
<ContainerHeader match={match} title={<IntlMessages id="sidebar.notification"/>}/>
<div className="row" style={{'flex-wrap': 'no wrap', "flex-direction": 'row'}}>
<div className="col-xl-7 col-lg-7 col-md-7 col-7">
<NotificationStepper titlechange={handleTitle} />
<div className='flex-class' style={{'width': '100%'}}>
<Button color="primary" style={{"align-self": "flex-end", "border" : "1px solid", "margin-left": "10px", "margin-bottom": "40px"}} size="small" className="col-md-2 col-2">Fetch</Button>
<Button color="primary" style={{"align-self": "flex-end", "border" : "1px solid", "margin-left": "10px", "margin-bottom": "40px"}} size="small" className="col-md-2 col-2" color="primary">Discard</Button>
</div>
</div>
<div className="col-xl-5 col-lg-5 col-md-5 col-5" style={{"padding-top": "20px"}}>
<span style={{"margin-left" : "20px", "font-weight": "bold"}}>Preview</span>
<TabComponent {...{[title]:titlename}} message={message} />
</div>
</div>
</div>
);
};
export default SendNotification;
and in TabComponent i am getting this prop value and using it component in following way
TabContainer.propTypes = {
children: PropTypes.node.isRequired,
dir: PropTypes.string.isRequired,
};
class TabComponent extends Component {
state = {
value: 0,
};
render() {
const {theme} = this.props;
const title = this.props.title;
return (
<div className="col-xl-12 col-lg-12 col-md-12 col-12" style={{"margin-top": "15px"}}>
<NotifCard key={0} data={{'name': title, 'company': 'sayge.ai', 'image': require("assets/images/bell.png"), 'description': this.props.message}} styleName="card shadow "/>
</div>
);
}
}
TabComponent.propTypes = {
theme: PropTypes.object.isRequired,
};
export default withStyles(null, {withTheme: true})(TabComponent);
StepperNotification is working fine and props are being updated in parent. i have checked this by printing the updated values in console, but the props in TabComponent are not being updated. What am i doing wrong here? any help is appreciated.
Probaly this is your issue
<TabComponent {...{[title]:titlename}} message={message} />` this could be just
title is undefined and you are sending props[undefined]=titlename
Just do this
<TabComponent title={titlename} message={message} />`
And if SendNotification is react fuction component useState for keeping track of current titlename. If it still doesn't work after first fix this second will be another source of your problems.

How to get ref of google-maps-react map and panto to latlng

My objective is to pan google-maps-react map to a latlng position, after getting a latlong from react-places-autocomplete when a user selects an address suggestion.
I am facing difficulty in setting ref of map from a child functional component, so that I can call map.panTo(location) in the parent functional component.
Following is my Google-Maps and PlaceAutoComplete child Component:
import React, { useEffect } from 'react';
import { Map, GoogleApiWrapper, Marker } from 'google-maps-react';
import { FormGroup, Label, Input, Spinner, Container, Row, Col } from 'reactstrap';
import PlacesAutocomplete from 'react-places-autocomplete';
const InputAndMap = React.forwardRef((props, ref) => {
return (
<div>
<PlacesAutocomplete
value={props.address}
onChange={props.handleInputChange}
onSelect={props.handleInputSelect}
>
{({ getInputProps, suggestions, getSuggestionItemProps, loading }) => (
<div>
<FormGroup>
<Label for="exampleSearch">Search Address</Label>
<Input
{...getInputProps({
className: 'location-search-input',
})}
type="search"
name="search"
id="exampleSearch"
placeholder="Enter Store Location"
/>
</FormGroup>
<div className="autocomplete-dropdown-container">
{loading && (
<div>
<Spinner size="sm" color="primary" />
Loading...
</div>
)}
{suggestions.map(suggestion => {
const className = suggestion.active ? 'suggestion-item--active' : 'suggestion-item';
const style = suggestion.active
? { backgroundColor: '#007bff', cursor: 'pointer', color: 'white' }
: { backgroundColor: '#ffffff', cursor: 'pointer' };
return (
<div
{...getSuggestionItemProps(suggestion, {
className,
style,
})}
>
<span>{suggestion.description}</span>
</div>
);
})}
</div>
</div>
)}
</PlacesAutocomplete>
<Row className="mb-3" style={{ width: '100%', height: '200px' }}>
<Col>
<Map
id="google-map"
ref={ref} // <<=== setting ref here
style={{ width: '100%', height: '200px' }}
google={props.google}
zoom={8}
initialCenter={{ lat: 47.444, lng: -122.176 }}
onClick={(t, map, e) => props.updateMarker(e.latLng, map)}
>
{props.markerLatLong && <Marker position={props.markerLatLong} />}
</Map>
</Col>
</Row>
</div>
);
});
export default GoogleApiWrapper({
apiKey: process.env.REACT_APP_GOOGLE_API_KEY,
libraries: ['places'],
})(InputAndMap);
This is my parent component, where I want to call the map panto function.
import React, { useState, useEffect } from 'react';
import { Button, Form, Spinner, Container } from 'reactstrap';
import { Redirect } from 'react-router-dom';
import { geocodeByAddress, getLatLng } from 'react-places-autocomplete';
import firebase from 'firebase/app';
import NavBarMenu from '../components/NavBarMenu';
import InputAndMap from '../components/InputAndMap';
import fire from '../config/fire';
function StoreScreen(props) {
const [isLoading, setIsLoading] = useState(false);
const [markerLatLong, setMarkerLatLong] = useState(null);
const [city, setCity] = useState('');
const [address, setAddress] = useState('');
const [redirect, setRedirect] = useState(false);
const ref = React.createRef();
const handleInputChange = address => {
setAddress(address);
};
const handleInputSelect = address => {
setAddress(address);
geocodeByAddress(address)
.then(results => {
processCity(results);
getLatLng(results[0])
.then(latLng => {
console.log('Success', latLng);
console.log(ref);// ==============> this return {current: null}
// ref.current.panTo(latLng);// ==> So I am unable to call this
})
.catch(error => console.error('Error', error));
})
.catch(error => console.error('Error', error));
};
return (
<div>
<NavBarMenu isShopKeeper />
<Container className="h-100">
<Form onSubmit={handleSubmit}>
<h5 className="text-center">Add Store</h5>
<InputAndMap
ref={ref}
markerLatLong={markerLatLong}
updateMarker={updateMarker}
handleInputChange={handleInputChange}
handleInputSelect={handleInputSelect}
address={address}
/>
{isLoading ? (
<div className="row mx-auto justify-content-center align-items-center flex-column">
<Spinner color="secondary" />
</div>
) : (
<Button
disabled={!markerLatLong || !city || !address}
className="mb-4"
color="primary"
size="lg"
block
>
Add Store
</Button>
)}
</Form>
</Container>
</div>
);
}
export default StoreScreen;
I am also attaching the image for better visualizing my problem.
Map.panTo changes the center of the map to the given LatLng in Maps JavaScript API. Since you are using google-maps-react library, you can use react states as value of the center parameter of this library to change the value of the Map's center everytime the state changes. In my example code below, I use the code from the getting started docs of react-places-autocomplete and incorporated it with a simple google-maps-react code.
Here's how I declare the state of the center which currently have a value:
state = {
center: {
lat: 40.854885,
lng: -88.081807
},
address: ""
};
Here's the handleSelect event from the react-places-autocomplete library where it geocodes the selected place from the autocomplete. Then you can see that I set the state of the center to the latLng of the geocoded address.
handleSelect = address => {
geocodeByAddress(address)
.then(results => getLatLng(results[0]))
.then(latLng => this.setState({ center: latLng }))
.catch(error => console.error("Error", error));
};
Here's how I call the Map component of the google-maps-react library where the value of center parameter is the value of the state named center.
<Map
className="map"
google={this.props.google}
onClick={this.onMapClicked}
center={this.state.center}
style={{ height: "100%", position: "relative", width: "100%" }}
zoom={13}
/>
Here's a complete code snippet and the working code on how I incorporated the 2 libraries you are using to change the center of the map everytime you choose an address from autocomplete:
import React, { Component } from "react";
import { Map, GoogleApiWrapper } from "google-maps-react";
import PlacesAutocomplete, {
geocodeByAddress,
getLatLng
} from "react-places-autocomplete";
export class MapContainer extends Component {
state = {
center: {
lat: 40.854885,
lng: -88.081807
},
address: ""
};
handleChange = address => {
this.setState({ address });
};
handleSelect = address => {
geocodeByAddress(address)
.then(results => getLatLng(results[0]))
.then(latLng => this.setState({ center: latLng }))
.catch(error => console.error("Error", error));
};
render() {
if (!this.props.loaded) return <div>Loading...</div>;
return (
<div>
<PlacesAutocomplete
value={this.state.address}
onChange={this.handleChange}
onSelect={this.handleSelect}
>
{({
getInputProps,
suggestions,
getSuggestionItemProps,
loading
}) => (
<div>
<input
{...getInputProps({
placeholder: "Search Places ...",
className: "location-search-input"
})}
/>
<div className="autocomplete-dropdown-container">
{loading && <div>Loading...</div>}
{suggestions.map(suggestion => {
const className = suggestion.active
? "suggestion-item--active"
: "suggestion-item";
// inline style for demonstration purpose
const style = suggestion.active
? { backgroundColor: "#fafafa", cursor: "pointer" }
: { backgroundColor: "#ffffff", cursor: "pointer" };
return (
<div
{...getSuggestionItemProps(suggestion, {
className,
style
})}
>
<span>{suggestion.description}</span>
</div>
);
})}
</div>
</div>
)}
</PlacesAutocomplete>
<Map
className="map"
google={this.props.google}
center={this.state.center}
style={{ height: "100%", position: "relative", width: "100%" }}
zoom={13}
/>
</div>
);
}
}
export default GoogleApiWrapper({
apiKey: "YOUR_API_KEY"
})(MapContainer);

Resources