2

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

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

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

我认为,我需要使用生命周期方法,但不知道如何实现它。或者,有没有更好的方法来使用 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>
));

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

4

1 回答 1

1

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

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

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

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 });
}

假设您正在调用getMarkers()您的DoctorSearchForm组件,您可以删除它,因为它会在接收新道具时自动更新标记 - 或者您可以完全绕过状态并render根据传入的props.

于 2018-02-06T04:16:54.967 回答