问题
I am using library react-google-maps, and i want to use DirectionsRenderer between two nodes. Here is my state
this.state={
currentPosition:{lat: 26.84 ,lng: 75.80},
destinationPosition:{lat: 26.84 ,lng: 75.80},
};
I want to show the direction between my current location and marker. And my componentDidMount() is inside render method. Here is the code for render method
class map extends React.PureComponent{
constructor(props){
super(props);
this.state={
currentPosition:{lat: 26.84 ,lng: 75.80},
destinationPosition:{lat: 26.84 ,lng: 75.80},
direction:false
};
}
onMarkerPositionChanged(e){
this.setState((state)=>({
destinationPosition:{lat:e.latLng.lat(),lng:e.latLng.lng()}}));
}
handleClick(){
if(navigator.geolocation){
navigator.geolocation.getCurrentPosition((position)=>{
this.setState(()=>({
currentPosition:{lat:position.coords.latitude,lng:position.coords.longitude}}))
});
}
else{
alert("Geoloaction is not supported by your browser");
}
}
changeDir(){
if(this.state.direction)
this.setState(()=>({direction:false}))
else
this.setState(()=>({direction:true}))
}
render(){
const MyMapComponent = compose(
withProps({
googleMapURL: "https://maps.googleapis.com/maps/api/js?key=AIzaSyC5VMMlyr_A6K5ycpOrq3OsVM8YYbn0q3A&v=3.exp&libraries=geometry,drawing,places",
loadingElement: <div style={{ height: `100%` }} />,
containerElement: <div style={{ height: `300px` }} />,
mapElement: <div style={{ height: `100%` }} />,
}),
withScriptjs,
withGoogleMap,
lifecycle({
componentDidMount() {
const google=window.google;
console.log(this.state);
//--->this statement prints null
const DirectionsService = new google.maps.DirectionsService();
DirectionsService.route({
origin: new google.maps.LatLng(this.state.currentPosition.lat, this.state.currentPosition.lng),
destination: new google.maps.LatLng(this.state.destinationPosition.lat,this.state.destinationPosition.lng),
//----> this is where i want to use the state to get the direction between //current location and marker
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={15} defaultCenter={this.state.destinationPosition} >
<Marker position={this.state.destinationPosition} draggable changeLat
onDragEnd={this.onMarkerPositionChanged.bind(this)}
/>
<Marker
icon="https://www.robotwoods.com/dev/misc/bluecircle.png"
position={this.state.currentPosition}
/>
{this.state.direction && props.directions && <DirectionsRenderer directions={props.directions} />}
<Button bsStyle="success" onClick={this.handleClick.bind(this)}>Current Position</Button>
<Button bsStyle="success" onClick={this.changeDir.bind(this)}>Get Direction</Button>
</GoogleMap>
);
return(
<Container state={this.state} map={MyMapComponent}/>
);
}
}
export default map;
when i use constant numbers in place of origin and destination it works fine.
回答1:
First things first, change the name of the component map
to Map
. Have a read at this SO post.
Now, regarding this:
when i use constant numbers in place of origin and destination it works fine.
The answer to this question has a background, continue reading the following so that it is more clear.
The problem of the this
context
In your code, you have a map
component, and you can access any of its properties (including the state) with this
. If you call this.state
inside the render
function for example, you will get the state of map
. You can use the componentDidMount
function of the map
component, to, for example, set a value in the state, like this:
class map extends Component {
constructor(props){
this.state = { myVal: 1}
}
componentDidMount(){
this.setState({ myVal: 2})
}
}
This code will not fail, because componentDidMount is a React function for the component, and this
can be used inside the component because it's in the same context. In this case, this.setState
will set the state of map
, because this
is map
.
This is the code of the example you provided:
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 />
In this example, this.setState
refers to the context of MapWithADirectionsRenderer
, so this.setState
is setting the state of MapWithADirectionsRenderer
, because this
is MapWithADirectionsRenderer
.
Now, in your code you are creating a new component called MyMapComponent
as a const object. This component lets you define the componentDidMount
function of that new component. That new component will have it's own context, so when you write this
inside the MyMapComponent
component, it will refer to MyMapComponent
, not to map
.
You cannot access the state of map
from within MyMapComponent
like that, you have to pass props to MyMapComponent
.
Solution
I created a couple of examples on how to pass values.
Solution 1
You can create a prop with any name (in this example it's called test with the value My value
):
const MyMapComponent = compose(
withProps({
googleMapURL:
"https://maps.googleapis.com/maps/api/js?key=AIzaSyC5VMMlyr_A6K5ycpOrq3OsVM8YYbn0q3A&v=3.exp&libraries=geometry,drawing,places",
loadingElement: <div style={{ height: `100%` }} />,
containerElement: <div style={{ height: `400px` }} />,
mapElement: <div style={{ height: `100%` }} />,
test:'My value'
}),
lifecycle({
componentDidMount() {
console.log(JSON.stringify(this.props.test));
}
}),
withScriptjs,
withGoogleMap,
)(props => (
<GoogleMap defaultZoom={8} defaultCenter={{ lat: -34.397, lng: 150.644 }}>
{props.isMarkerShown && (
<Marker position={{ lat: -34.397, lng: 150.644 }} />
)}
</GoogleMap>
));
This way, you can log this.props.test
with the given value, whatever you use.
Solution 2
If what you need is to render the component depending on the value found in the state of Map
, use something like this.
You can pass props to MyMapComponent
an then access them using props.
and adding the name of the prop.
For example if you pass a new prop called latProp
like this:
<MyMapComponent isMarkerShown latProp={-34.397} />
You can access it like this:
(props => (
<GoogleMap defaultZoom={8} defaultCenter={{ lat: -34.397, lng: 150.644 }}>
{props.isMarkerShown && (
<Marker position={{ lat: props.latProp, lng: 150.644 }} />
)}
</GoogleMap>
));
Since what you want is to replace the constant value with what you have in the state, just replace the values sent in props.latProp
with whatever property you have in the state:
<MyMapComponent isMarkerShown latProp={this.state.lat} />
This is the complete component:
const MyMapComponent = compose(
withProps({
googleMapURL:
"https://maps.googleapis.com/maps/api/js?key=AIzaSyC5VMMlyr_A6K5ycpOrq3OsVM8YYbn0q3A&v=3.exp&libraries=geometry,drawing,places",
loadingElement: <div style={{ height: `100%` }} />,
containerElement: <div style={{ height: `400px` }} />,
mapElement: <div style={{ height: `100%` }} />,
}),
withScriptjs,
withGoogleMap,
)(props => (
<GoogleMap defaultZoom={8} defaultCenter={{ lat: -34.397, lng: 150.644 }}>
{props.isMarkerShown && (
<Marker position={{ lat: props.latProp, lng: props.lngProp }} />
)}
</GoogleMap>
));
class App extends Component{
constructor(props){
super(props);
this.state = { lat: -34.400, lng: 151.644 }
}
render(){
return (
<div>
<MyMapComponent isMarkerShown latProp={this.state.lat} lngProp={this.state.lng} />
</div>
);
}
}
export default App;
来源:https://stackoverflow.com/questions/53077608/cannot-access-state-in-componentdidmount