ReactJS-似乎无法按期望的顺序运行命令

时间:2020-04-24 23:17:15

标签: javascript reactjs

我正在用ReactJS制作附近的餐馆应用。我正在通过搜索输入使用Google地方信息自动填充功能。我正在尝试进行地理位置定位,似乎运行顺利。

出问题的是,每当我在自动完成下拉列表中选择一个位置时,我都会运行一个传递查询字符串以转换为地理位置的函数,我的搜索结果始终呈现以前的经度和纬度状态,而不是在该函数的最后一次运行中获取的那个。

我似乎无法正确执行这些命令的执行顺序。

我正在谈论的功能如下:

  handlePlaceSelect = () => {
    // Extract City From Address Object
    const addressObject = this.autocomplete.getPlace();
    const address = addressObject.address_components;
    const URL = `https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=${
      this.state.lat
    },${this.state.lng}&type=restaurant&radius=${5 *
      1000}&key=MYAPIKEY`;

    Geocode.setApiKey('MYAPIKEY');

    // Check if address is valid
    if (address) {
      // Set State
      this.setState({
        city: address[0].long_name,
        query: addressObject.formatted_address
      });
    }

    Geocode.fromAddress(this.state.query)
      .then(
        response => {
          const { lat, lng } = response.results[0].geometry.location;
          this.setState({
            lat: lat,
            lng: lng
          });
        },
        error => {
          console.error(error);
        }
      )
      .then(
        axios
          .get(URL)
          .then(response => {
            console.log(response.data);
            this.setState({ places: response.data.results });
          })
          .catch(error => {
            console.log(error.message);
          })
      );
  };

这是此组件的其余代码:

import React, { Component } from 'react';

// Imports
import axios from 'axios';
import Script from 'react-load-script';
import { Button, SearchInput } from 'evergreen-ui';
import ResultsItem from '../../components/ResultsItem/ResultsItem.jsx';

import Geocode from 'react-geocode';

// Styles
import './Search.scss';

class Autocomplete extends Component {
  // Define Constructor
  constructor(props) {
    super(props);

    // Declare State
    this.state = {
      type: 'restaurant',
      radius: 10,
      lat: '59.0738',
      lng: '41.3226',
      city: '',
      query: '',
      open: false,
      places: []
    };

    this.currentLocationOnClick = this.currentLocationOnClick.bind(this);
    this.handlePlaceSelect = this.handlePlaceSelect.bind(this);
  }

  currentLocationOnClick() {
    navigator.geolocation.getCurrentPosition(
      position => {
        this.setState({ lat: position.coords.latitude });
        this.setState({ lng: position.coords.longitude });
      },
      error => {
        console.log('Error getting location');
      }
    );
  }

  async componentDidMount() {
    const url = `https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=
    ${this.state.lat},${this.state.lng}type=restaurant&radius=${2 *
      1000}&key=MYAPIKEY`;
    const response = await fetch(url);
    const data = await response.json();
    this.setState({ places: data.results });
    console.log(data.results);
  }

  handleScriptLoad = () => {
    // Declare Options For Autocomplete
    const options = {
      types: ['address']
    }; // To disable any eslint 'google not defined' errors

    // Initialize Google Autocomplete
    /*global google*/ this.autocomplete = new google.maps.places.Autocomplete(
      document.getElementById('autocomplete'),
      options
    );

    // Avoid paying for data that you don't need by restricting the set of
    // place fields that are returned to just the address components and formatted
    // address.
    this.autocomplete.setFields(['address_components', 'formatted_address']);

    // Fire Event when a suggested name is selected
    this.autocomplete.addListener('place_changed', this.handlePlaceSelect);
  };

  handlePlaceSelect = () => {
    // Extract City From Address Object
    const addressObject = this.autocomplete.getPlace();
    const address = addressObject.address_components;
    const URL = `https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=${
      this.state.lat
    },${this.state.lng}&type=restaurant&radius=${5 *
      1000}&key=MYAPIKEY`;

    Geocode.setApiKey('MAAPIKEY');

    // Check if address is valid
    if (address) {
      // Set State
      this.setState({
        city: address[0].long_name,
        query: addressObject.formatted_address
      });
    }

    Geocode.fromAddress(this.state.query)
      .then(
        response => {
          const { lat, lng } = response.results[0].geometry.location;
          this.setState({
            lat: lat,
            lng: lng
          });
        },
        error => {
          console.error(error);
        }
      )
      .then(
        axios
          .get(URL)
          .then(response => {
            console.log(response.data);
            this.setState({ places: response.data.results });
          })
          .catch(error => {
            console.log(error.message);
          })
      );
  };

  render() {
    const findPlacesOnClick = () => {
      const URL = `https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=${
        this.state.lat
      },${this.state.lng}&type=restaurant&radius=${5 *
        1000}&key=MYAPIKEY`;
      axios
        .get(URL)
        .then(response => {
          console.log(response.data);
          this.setState({ places: response.data.results });
        })
        .catch(error => {
          console.log(error.message);
        });
    };

    return (
      <div>
        <div className="search">
          <SearchInput
            id="autocomplete"
            placeholder="Search by address"
            width="100%"
            height={56}
          />
          <Button onClick={this.currentLocationOnClick}>
            {this.state.lat} & {this.state.lng}
          </Button>

          <Button appearance="primary" onClick={findPlacesOnClick}>
            Fetch
          </Button>

          <Script
            url="https://maps.googleapis.com/maps/api/js?key=MYAPIKEY&libraries=places,geometry&callback=initAutocomplete"
            onLoad={this.handleScriptLoad}
          />
        </div>

        <div className="results">
          {this.state.places.map(places => (
            <div className="results-item" onClick={this.props.sideBarOpen}>
              <ResultsItem name={places.name} />
            </div>
          ))}
        </div>
      </div>
    );
  }
}

export default Autocomplete;


1 个答案:

答案 0 :(得分:0)

setState函数可以是异步。在下一次渲染之前,this.state对象很可能不会使用传递给this.setState的对象进行更新。

解决方法是将要发生的事情放在状态更改后的在回调中:

this.setState({
  city: address[0].long_name,
  query: addressObject.formatted_address
}, state => {
  // the rest of the code
});

...或仅坚持原子状态更改。如果您不需要在两个setState调用之间重新渲染,则首选此方法:

handlePlaceSelect = async () => {
  let { query } = this.state;

  // Extract City From Address Object
  const addressObject = this.autocomplete.getPlace();
  const address = addressObject.address_components;
  Geocode.setApiKey('MYAPIKEY');

  // Check if address is valid
  let city;
  if (address) {
    city = address[0].long_name;
    query = addressObject.formatted_address
  }

  let lat, lng;
  try {
    const response = await Geocode.fromAddress(query);
    ({ lat, lng } = response.results[0].geometry.location);
  } catch (error) {
    console.error(error);
  }

  let places;
  try {
    const URL = `https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=${lat},${lng}&type=restaurant&radius=${5 * 1000}&key=MYAPIKEY`;
    const response = await axios.get(URL)
    console.log(response.data);
    places = response.data.results;
  } catch (err) {
    console.log(error.message);
  }

  this.setState({ query, places, city, lat, lng });
}
相关问题