не может получить доступ к состоянию в componentDidMount - PullRequest
0 голосов
/ 31 октября 2018

Я использую библиотеку реагировать-Google-карты, и я хочу использовать DirectionsRenderer между двумя узлами. Вот мое состояние

this.state={
    currentPosition:{lat: 26.84 ,lng: 75.80},
    destinationPosition:{lat: 26.84 ,lng: 75.80},
};

Я хочу показать направление между моим текущим местоположением и маркером. И мой componentDidMount () находится внутри метода рендеринга. Вот код для метода рендеринга

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;

когда я использую постоянные числа в месте отправления и назначения, все работает нормально.

1 Ответ

0 голосов
/ 31 октября 2018

Перво-наперво, измените имя компонента map на Map. Прочитайте на этом посте .

Теперь относительно этого:

когда я использую постоянные числа в месте отправления и назначения, все работает нормально.

Ответ на этот вопрос имеет подоплеку, продолжайте читать, чтобы он был более понятным.

Проблема контекста this

В вашем коде у вас есть компонент map, и вы можете получить доступ к любым его свойствам (включая состояние) с помощью this. Например, если вы вызовете this.state внутри функции render, вы получите состояние map. Вы можете использовать функцию componentDidMount компонента map, например, чтобы установить значение в состоянии, например:

class map extends Component {

  constructor(props){
    this.state = { myVal: 1}
  }

  componentDidMount(){
    this.setState({ myVal: 2})
  }
}

Этот код не завершится сбоем, так как componentDidMount является функцией React для компонента, и this можно использовать внутри компонента, поскольку он находится в том же контексте. В этом случае this.setState установит состояние map, поскольку this равно map.

Это код приведенного вами примера :

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 />

В этом примере this.setState относится к контексту MapWithADirectionsRenderer, поэтому this.setState устанавливает состояние MapWithADirectionsRenderer, потому что this равно MapWithADirectionsRenderer.

Теперь в своем коде вы создаете новый компонент с именем MyMapComponent в качестве объекта const. Этот компонент позволяет вам определить функцию componentDidMount этого нового компонента. Этот новый компонент будет иметь собственный контекст, поэтому, когда вы пишете this внутри компонента MyMapComponent, он будет ссылаться на MyMapComponent, а не на map.

Вы не можете получить доступ к состоянию map изнутри MyMapComponent таким образом, вы должны передать реквизиты MyMapComponent.

Решение

Я создал несколько примеров того, как передавать значения.

Решение 1

Вы можете создать реквизит с любым именем (в данном примере это называется test со значением 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.props.test с заданным значением, что бы вы ни использовали.

Решение 2

Если вам нужно визуализировать компонент в зависимости от значения, найденного в состоянии Map, используйте что-то вроде этого.

Вы можете передать реквизиты MyMapComponent, а затем получить к ним доступ, используя props. и добавив название реквизита.

Например, если вы передадите новую опору с именем latProp, например:

<MyMapComponent isMarkerShown latProp={-34.397} />

Вы можете получить к нему доступ так:

(props => (
  <GoogleMap defaultZoom={8} defaultCenter={{ lat: -34.397, lng: 150.644 }}>
    {props.isMarkerShown && (
      <Marker position={{ lat: props.latProp, lng: 150.644 }} />
    )}
  </GoogleMap>
));

Поскольку вам нужно заменить значение константы тем, что у вас есть в состоянии, просто замените значения, отправленные в props.latProp, на любое свойство, которое вы имеете в состоянии:

<MyMapComponent isMarkerShown latProp={this.state.lat} />

Это полный компонент:

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;
...