【问题标题】:react-google-maps render markers after user submission用户提交后 react-google-maps 渲染标记
【发布时间】:2018-07-16 01:08:26
【问题描述】:

这里是 React/Redux 新手。我有一个表单输入,允许用户输入医生问题。它通过 Redux 操作从服务器返回医生列表,并在地图上显示医生列表和每个医生位置的标记 (react-google-maps)。

当我点击提交时,显示正确问题的医生列表,地图在那里,但没有标记。只有在提交表格后,我才能在地图上显示标记,然后单击列表中的医生以显示他们的详细信息。

想要:输入一个医生问题,并在用户单击提交时在地图上呈现医生列表和标记。然后,选择一个医生来查看他们的详细信息页面(这是另一个问题,路由到动态详细信息页面)。

我认为,我需要使用生命周期方法,但不确定如何实现它。或者,有没有更好的方法来使用 Redux 处理这个问题?

医生组件:

import React, { Component } from 'react';
import PropTypes from 'prop-types';

import DoctorSearchForm from '../../containers/doctors/DoctorSearchForm';
import DoctorList from './DoctorList';
import Map from '../maps/Map';


class Doctors extends Component {

  constructor(props) {
    super(props);
    this.state = {
      markers: [],
      isMarkerShown: false
    }
  }

  componentDidMount() {
    this.getMarkers();
  }

  getMarkers = () => {
    let practices = this.props.doctors.map(function(doctor, index) {
      return {
        title: doctor.profile.first_name + ' ' + doctor.profile.last_name,
        location: {
          lat: doctor.practices[0].visit_address.lat,
          lng: doctor.practices[0].visit_address.lon
        }
      }
    });
    this.setState({ markers: practices, isMarkerShown: true });
  }

  render() {
    const { doctors, match } = this.props;

    return (
      <div>
        <DoctorSearchForm getMarkers={this.getMarkers} />
        <div className="row">
          <div className="col-md-4">
            <DoctorList doctors={doctors} match={match} />
          </div>
          <div className="col-md-8">
            <Map
              isMarkerShown={this.state.isMarkerShown}
              center={{ lat: 45.6318,lng: -122.6716 }}
              zoom={12}
              markers={this.state.markers}
              />
          </div>
        </div>
      </div>
    );
  }
}

Doctors.propTypes = {
  doctors: PropTypes.array.isRequired,
  match: PropTypes.object.isRequired
}

export default Doctors;

DoctorList 组件:

import React from "react";
import { Route } from 'react-router-dom';

import DoctorItem from './DoctorItem';
import DoctorView from './DoctorView';


class DoctorList extends React.Component {
  render() {
    const { doctors, match } = this.props;
    const linkList = doctors.map((doctor, index) => {
      return (
        <DoctorItem doctor={doctor} match={match} key={index} />
      );
    });

    return (
      <div>
        <h3>DoctorList</h3>
        <ul>{linkList}</ul>
        <Route path={`${match.url}/:name`}
          render={ (props) => <DoctorView data= {this.props.doctors} {...props} />}
          />
        </div>
      );
  }
}

export default DoctorList;

DoctorItem 组件:

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Link, Route } from 'react-router-dom';

import DoctorView from './DoctorView';


const DoctorItem = (props) => {
  const { doctor, match } = props;
    return (
      <li>
        <Link to={{ pathname: `${match.url}/${doctor.profile.first_name}-${doctor.profile.last_name}` }}>
          {doctor.profile.first_name} {doctor.profile.last_name}
        </Link>
      </li>
    )
  }

  DoctorItem.propTypes = {
    doctor: PropTypes.object.isRequired,
  };

  export default DoctorItem;

DoctorView 组件:

import React from 'react';


const DoctorView = ({ data, match }) => {
  const doctor = data.find(p => `${p.profile.first_name}-${p.profile.last_name}` === match.params.name);


  let doctorData;

  if (doctor) {
    const mappedSpecialties = Object.entries(doctor.specialties).map(([index, specialty]) => {
      return <li key={index} id={index}>{specialty.description}</li>;
      });
      doctorData =
      <div>
        <h5><strong>{doctor.profile.first_name} {doctor.profile.last_name}</strong> - {doctor.profile.title}</h5>
        <img src={doctor.profile.image_url} alt={"Dr." + doctor.profile.first_name + " " + doctor.profile.last_name} />
        <ul>{mappedSpecialties}</ul>
        <p>{doctor.profile.bio}</p>
      </div>;
    }
    return (
      <div>
        {doctorData}
      </div>
    )
  }

  export default DoctorView;

地图组件:

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { withScriptjs, withGoogleMap, GoogleMap, Marker } from 'react-google-maps';
import { compose, withProps } from "recompose";


export default Map = compose(
  withProps({
    googleMapURL:
      "https://maps.googleapis.com/maps/api/js?key={MY_KEY}&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={9} defaultCenter={{ lat: 45.6318,lng: -122.6716 }}>
    {props.markers.map((doctor, index) => {
          const marker = {
            position: { lat: doctor.location.lat, lng: doctor.location.lng },
            title: doctor.title
          }
          return <Marker key={index} {...marker} />;
        })}
  </GoogleMap>
));

我花了几天时间尝试和寻找答案,但没有成功。任何帮助将不胜感激!

【问题讨论】:

    标签: javascript forms reactjs redux react-google-maps


    【解决方案1】:

    就像你在组件挂载时计算标记一样,当你收到新的道具时你需要重新计算标记:

    componentWillReceiveProps(nextProps) {
       this.getMarkers(nextProps);
    }
    

    这将要求您稍微更改您的 getMarkers 签名,以便它可以接受参数并在您的 map 操作中使用该参数而不是 this.props:

    getMarkers = (props = this.props) => {
      let practices = props.doctors.map(function(doctor, index) {
        return {
          title: doctor.profile.first_name + ' ' + doctor.profile.last_name,
          location: {
            lat: doctor.practices[0].visit_address.lat,
            lng: doctor.practices[0].visit_address.lon
          }
        }
      });
      this.setState({ markers: practices, isMarkerShown: true });
    }
    

    假设您在 DoctorSearchForm 组件中调用 getMarkers(),您可以删除它,因为它会在接收新道具时自动更新标记 - 或者您可以完全绕过状态并在 @987654328 中即时计算它@ 基于传入的props。

    【讨论】:

    • 谢谢你,Rob,它成功了!因此,如果我理解正确,componentWillReceiveProps 方法将侦听 getMarkers 中的更改(将当前道具与新道具进行比较),然后在发现差异时替换 getMarkers 函数中的道具?
    • 很高兴为您提供帮助!定义后,只要组件从父级接收到新道具,就会自动调用componentWillReceiveProps。使用此设置,getMarkers 将使用新道具(并更新 doctors 列表)调用,这将在 Doctors 组件中调用 setState,触发使用最新列表和地图的重新渲染。设置状态和接收道具之间可能存在竞争条件,导致this.state.markers 落后一步。你会发现在使用 redux 时,在组件中管理状态变得没有意义(而且更混乱)
    • 很好的解释,现在真的很有意义。再次感谢!
    猜你喜欢
    • 2022-08-22
    • 1970-01-01
    • 2018-07-18
    • 2016-10-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-29
    相关资源
    最近更新 更多