ReactJS Passing Value to Google Map Component - reactjs

I have the following GoogleMaps React component:
var GoogleMaps = React.createClass({
getDefaultProps: function () {
return {
initialZoom: 6,
mapCenterLat: 53.5333,
mapCenterLng: -113.4073126
};
},
componentDidMount: function (rootNode) {
var mapOptions = {
center: this.mapCenterLatLng(),
zoom: this.props.initialZoom
},
map = new google.maps.Map(this.getDOMNode(), mapOptions);
var marker = new google.maps.Marker({position: this.mapCenterLatLng(), title: 'Hi', map: map});
this.setState({map: map});
},
mapCenterLatLng: function () {
var props = this.props;
return new google.maps.LatLng(props.mapCenterLat, props.mapCenterLng);
},
render: function () {
return (
<div className='map-gic'></div>
);
}
});
And then use it via the following declaration:
var myPage = React.createClass({
render: function() {
return (
<div>
<GoogleMaps mlat="55.0000" mlong="-113.0000" />
</div>
)
}
});
How do I access the passed values (mlat and mlong) so that the location would be marked on the rendered map? At the moment, the values in the getDefaultProps function are the ones being marked on the map.
Rephrase the question:
How do I replace the value of the variables mapCenterLat and mapCenterLng in the getDefaultProps function with the values I pass through the component rendering?
When I check the value passed in the render function of the GoogleMaps component by adding some console.logs like this:
render: function () {
console.log(this.props.mlat);
console.log(this.props.mlong);
return (
<div className='map-gic'></div>
)
}
This is what registers in the browser console:
undefined
undefined
undefined
undefined
55.0000
113.000
The source code for this application is in this GitHub repo.

Here is a basic working example.
I'm not sure what you're code is doing wrong as it does not entirely match what it would need to be to work or demonstrate the problem.
I've not attempted to clean up the code further. It doesn't work if the props change (as componentDidMount only executes once), and doesn't properly remove the Map when React executes an unmount on the control.
var GoogleMap = React.createClass({
getDefaultProps: function () {
return {
initialZoom: 8,
mapCenterLat: 41.8, /* Chicago by default */
mapCenterLng: -87.6,
};
},
componentDidMount: function (rootNode) {
var mapOptions = {
center: this.mapCenterLatLng(),
zoom: this.props.initialZoom
};
var map = new google.maps.Map(this.getDOMNode(), mapOptions);
var marker = new google.maps.Marker({position: this.mapCenterLatLng(), map: map});
this.setState({map: map});
},
mapCenterLatLng: function () {
var props = this.props;
return new google.maps.LatLng(
props.mapCenterLat, props.mapCenterLng);
},
render: function () {
return (
<div className="gmap"></div>
);
}
});
// Bay area override
React.renderComponent(
<GoogleMap mapCenterLat='37' mapCenterLng='-122' />,
document.body);

Related

How to use react to implement google Place's Searches, to create markers on the map?

Im new with react and i have created a react project. I would like to know how i can use this default starter project to implement the code from:
code link from google. The code is as the following, credit to google, linked above.
Css file-
/* Always set the map height explicitly to define the size of the div
* element that contains the map. */
#map {
height: 100%;
}
/* Optional: Makes the sample page fill the window. */
html, body {
height: 100%;
margin: 0;
padding: 0;
}
HTML file-
<div id="map"></div>
<!-- Replace the value of the key parameter with your own API key. -->
<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk&libraries=places&callback=initMap" async defer></script>
Java script (pure js) file -
// This example requires the Places library. Include the libraries=places
// parameter when you first load the API. For example:
// <script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=places">
var map;
var service;
var infowindow;
function initMap() {
var sydney = new google.maps.LatLng(-33.867, 151.195);
infowindow = new google.maps.InfoWindow();
map = new google.maps.Map(
document.getElementById('map'), {center: sydney, zoom: 15});
var request = {
query: 'Museum of Contemporary Art Australia',
fields: ['name', 'geometry'],
};
service = new google.maps.places.PlacesService(map);
service.findPlaceFromQuery(request, function(results, status) {
if (status === google.maps.places.PlacesServiceStatus.OK) {
for (var i = 0; i < results.length; i++) {
createMarker(results[i]);
}
map.setCenter(results[0].geometry.location);
}
});
}
function createMarker(place) {
var marker = new google.maps.Marker({
map: map,
position: place.geometry.location
});
google.maps.event.addListener(marker, 'click', function() {
infowindow.setContent(place.name);
infowindow.open(map, this);
});
}
I understand i need to create my key with google maps js and google places, however since Im new to react I'm unsure to how i could implement this into my new react project. Coudl some one show me how these files of code could be put together to be fit for a react project please. I apologies if i sound all over the place.
You can refer to this code I made. Remember to change the value of "YOUR_API_KEY" so that the map will work properly.
Here is the App.js code snippet:
import React from 'react';
import './App.css';
import Map from './components/placeSearch';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
map: {}
}
}
handleMapLoad = (map) => {
this.setState({
map: map
})
}
render() {
return (
<div className="App">
<Map id="myMap" options={{center: { lat: 51.501904, lng: -0.115871 }, zoom: 13}} onMapLoad = {this.handleMapLoad}/>
</div>
);
}
}
export default App;
The code for Place Search can be found in the Map components in placeSearch.js. Change the value of your API key here.
import React from "react";
import ReactDOM from 'react-dom';
const map;
var markers = [];
var infowindow;
const API_KEY = "YOUR_API_KEY";
var place = [];
class Map extends React.Component {
constructor(props) {
super(props);
}
componentDidMount() {
const script = document.createElement('script');
script.type = 'text/javascript';
script.src = `https://maps.googleapis.com/maps/api/js?key=` + API_KEY + `&libraries=geometry,places`;
script.id = 'googleMaps';
script.async = true;
script.defer = true;
document.body.appendChild(script);
script.addEventListener('load', e => {
this.onScriptLoad()
})
}
onScriptLoad() {
map = new window.google.maps.Map(document.getElementById(this.props.id), this.props.options);
this.props.onMapLoad(map)
var request = {
query: 'Museum of Contemporary Art Australia',
fields: ['name', 'geometry'],
};
var service = new google.maps.places.PlacesService(map);
service.findPlaceFromQuery(request, function(results, status) {
if (status === google.maps.places.PlacesServiceStatus.OK) {
for (var i = 0; i < results.length; i++) {
var place = results[i];
var marker = new google.maps.Marker({
map: map,
position:place.geometry.location,
title: place.formatted_address,
});
markers.push(marker);
infowindow = new google.maps.InfoWindow();
marker.addListener('click', () => {
infowindow.setContent(place.name);
infowindow.open(map, marker);
});
}
map.setCenter(results[0].geometry.location);
}
})
}
render() {
return (
<div id = "root" >
<div className = "map" id = {this.props.id}/>
</div>
)
}
}
export default Map;
Hope this helps!

React component props not updating

Hello Im creating a simple React Component with just a label that change its content when a SignalR method is fired. My react component is like this one:
var PersonalityStatusApp = React.createClass({
getInitialState: function () {
return { data: dataInit };
},
componentWillMount(){
var self = this;
this.setState({ data:this.props.status});
Votinghub.on("UpdateStatusLabel", function (data) {
var obj = $.parseJSON(data);
self.setState({ data: obj });
});
},
render: function () {
return (
<div className="PersonalityStatusApp">
<label>{this.props.status}</label>
</div>
);
}
});
When te component receives a UpdateStatusLabel signalR message it change the State of the component with the value that gets from the signalR message.
The method UpdateStatusLabel gets the correct value.
This fires the render method, but when I check the properties in the render method I see thnat the values are still the ones from the initial state.
Can somebody help me?
Reason is, you are updating the state variable and printing the props value. Initially state variable data will have the value of this.props and after you get the signalR you are updating the state by data: obj, so print the value of this.stat.data.status it will print the updated value.
use this:
return (
<div className="PersonalityStatusApp">
<label>{this.state.data.status}</label>
</div>
);
Note: Initially you need to set the value of data: this.props
Full part:
var PersonalityStatusApp = React.createClass({
getInitialState: function () {
return { data: dataInit };
},
componentWillMount(){
var self = this;
this.setState({ data: this.props}); //changed this
Votinghub.on("UpdateStatusLabel", function (data) {
var obj = $.parseJSON(data);
self.setState({ data: obj });
});
},
render: function () {
return (
<div className="PersonalityStatusApp">
<label>{this.state.data.status}</label>
</div>
);
}
});

Handling the screen orientation change in ReactJS

I am trying to create a Component whose content changes when the screen orientation(portrait/landscape) changes. This is what I am doing:
var Greeting = React.createClass({
getInitialState: function() {
return {orientation: true}
},
handleChange: function() {
if ('onorientationchange' in window) {
window.addEventListener("orientationchange", function() {
this.setState({
orientation: !this.state.orientation
})
console.log("onorientationchange");
}, false);
} else if ('onresize' in window) {
window.addEventListener("resize", function() {
this.setState({
orientation: !this.state.orientation
})
console.log("resize");
}, false);
}
},
render: function() {
var message = this.state.orientation ? "Hello" : "Good bye"
return <p>{message}</p>;
}
});
ReactDOM.render(
<Greeting/>, document.getElementById('container'));
How to make sure the state is mutated when the orientation change event is fired .
Your calling to this.setState is wrong. Need to change it to be like:
handleChange: function() {
var self = this; // Store `this` component outside the callback
if ('onorientationchange' in window) {
window.addEventListener("orientationchange", function() {
// `this` is now pointing to `window`, not the component. So use `self`.
self.setState({
orientation: !self.state.orientation
})
console.log("onorientationchange");
}, false);
}

React testing 'componentWillReceiveProps'

I am a newbie in React.js. While trying to understand the lifecycles in React, i stumbled upon componentWillReceiveProps. Even though i got hold of other functions, i am still not able to figure out componentWillReceiveProps. I created a small snippet where on every button click, i am incrementing the variable 'val'. When val becomes a multiple of 5, i want to change the value of 'increasing', which i am not able to do.
My Code is:
var Increment = React.createClass({
getInitialState: function() {
return {val: 0, increasing: false};
},
componentWillMount: function() {
console.log("componentWillMount");
},
componentDidMount: function() {
console.log("componentDidMount");
},
handleClick: function() {
console.log("inHandleClick");
console.log(this.state.val);
this.setState({val: (this.state.val+1)});
},
componentWillReceiveProps : function(nextProps) {
this.setState({
increasing: (nextProps.val > this.props.val)
});
},
shouldComponentUpdate: function(nextProps, nextState) {
return (nextState.val % 5 ==0)
},
render: function() {
console.log(this.state.increasing);
return (
<div>
<button onClick={this.handleClick}>{this.state.val}</button>
</div>
);
}
});
ReactDOM.render(<Increment />, mountNode);
Any Leads? Thanks in advance
See this fiddle
var IncrementButton = React.createClass({
componentWillReceiveProps : function(nextProps) {
this.setState({
increasing: (nextProps.val > this.props.val)
});
},
render: function() {
return (<button onClick={this.props.handleClick}>{this.props.val}</button>);
}
});
var Increment = React.createClass({
getInitialState: function() {
return {val: 0, increasing: false};
},
handleClick: function() {
console.log("inHandleClick");
console.log(this.state.val);
this.setState({val: (this.state.val+1)});
},
shouldComponentUpdate: function(nextProps, nextState) {
return (nextState.val % 5 ==0)
},
render: function() {
console.log(this.state.increasing);
return (
<div>
<IncrementButton handleClick={this.handleClick} val={this.state.val}/>
</div>
);
}
});
React.render(<Increment />, mountNode);
(Thanks to #Aaron for a more accurate description below)
componentWillReceiveProps is called if your component's props get set; note it may be called even though the props haven't changed in value (You seem to take this into account with your greater than check). Since you want to compare a new value of props to an old value, you need the state to be managed outside your component. I have therefore, in this sample, split your component into two pieces by extracting the button.

How to manage UI feedback?

Is there an established pattern used to manage user interactions with individual components, such as displaying loader spinners, disabling input fields while a form is saving/loading, etc.?
I'm finding myself doing the following in my stores in order to keep components somewhat decoupled from any implied state:
function CampaignStore() {
EventEmitter.call(this);
AppDispatcher.register(payload => {
switch (payload.type) {
// [#1] ---------------v (main action)
case CampaignContants.SAVE:
// [#2] ------------------------v (prepare for the main action)
this.emit(CampaignContants.WILL_SAVE);
const data = payload.data;
if (data.id) {
// [#3] ---v (perform main action in store)
updateCampaign(payload.data).then(_ => {
// [#4] ------------------------v (after main action)
this.emit(CampaignContants.DID_SAVE, 0)
});
} else {
insertCampaign(payload.data).then(campaignId => this.emit(CampaignContants.DID_SAVE, campaignId));
}
break;
// ...
}
}
}
Basically, I just fire an event saying that some action is about to take place, then I perform the action (make API call, etc.), then emit another event when the action completes.
Inside a component, I can just subscribe to a WILL_<action> event, render all the spinners and such, then clear up the screen when the DID_<action> is fired. While this seems to work, it does feel pretty boilerplattie and repetitive, as well as super messy (way too much state that only exists to tweak the UI based on where an action is (between WILL_<action> and *DID_<action>.
// some component
var MyComponent = React.createClass({
getInitialState: function () {
return {
items: [],
loading: false,
saving: false,
checkingPasswordStrength: fase,
// ...
};
},
render: function(){
return (
<div>
{this.state.loading && (
<p>Loading...</p>
)}
{!this.state.loading && (
// Display component in not-loading state
)}
</div>
);
}
});
I think you would be better off using the lifecycle methods such as componentWillMount, componentDidMount, componentWillUpdate, and componentWillUnmount. Using those methods you can inspect the previous/current/next props/state (depending on the method) and respond to that. That way your store only handles your state, and your components become more pure.
we have found a simple loading container component helps here.
so something like this:
const LoadingContainer = React.createClass({
getDefaultProps: function() {
return {isLoadedCheck:(res) => res.data!=null }
},
getInitialState: function() {
return {isLoaded:false, errors:[]}
},
componentDidMount: function() {
if(this.props.initialLoad) { this.props.initialLoad(); }
if(this.props.changeListener) { this.props.changeListener(this.onChange); }
},
onChange: function() {
let res = this.props.loadData();
this.setState({errors: res.errors, isLoaded: this.props.isLoadedCheck(res)});
},
render: function() {
if(!this.state.isLoaded) {
let errors = this.state.errors && (<div>{this.state.errors.length} errors</div>)
return (<div>{errors}<LoadingGraphic /> </div>)
}
return <div>{this.props.children}</div>
}
});
const Wrapper = React.createClass({
getDefaultProps: function() {
return {id:23}
},
render: function() {
let initialLoad = () => someActionCreator.getData(this.props.id);
let loadData = () => someStore.getData(this.props.id);
let changeListener = (fn) => someStore.onChange(fn);
return (<div><LoadingContainer initialLoad={initialLoad}
changeListener={changeListener}
loadData={loadData}
isLoadedCheck={(res) => res.someData != null}><SomeComponent id={this.props.id} /></LoadingContainer></div>)
}
});
while it adds another stateless wrapper, it gives a clean way to make sure your components dont just load on mount and a common place to show api feedback etc.
and with react 14 these sort of pure stateless wrappers are getting a bit of a push, with perf improvements to come, so we've found it scales nicely
This is the pattern which will help you in getting your individual components to manage user interactions
var MyComponent = React.createClass({
getInitialState: function() {
return {
item: [],
loading: true,
};
},
componentDidMount: function() {
//Make your API calls here
var self = this;
$.ajax({
method: 'GET',
url: 'http://jsonplaceholder.typicode.com/posts/1',
success: function(data) {
if (self.isMounted()) {
self.setState({
item: data,
loading: false
});
}
}
});
},
render: function() {
var componentContent = null;
if (this.state.loading) {
componentContent = (<div className="loader"></div>);
} else {
componentContent = (
<div>
<h4>{this.state.item.title}</h4>
<p>{this.state.item.body}</p>
</div>
);
}
return componentContent;
}});

Resources