1

I am trying to add a “Select button” to a dialog box that pops out from a component using the google map API, inside ClinicMap.js. I have implemented the same “Select button” on CompareDialog.js but for some reason, it works there but not on ClinicMap.js. I got the error Invariant failed: You should not use <Route> outside a <Router> and I referred to this but do not understand how the solution applies to my context.

Can someone enlighten me what could be the fix for this?

Here’s some of the relevant code, feel free to ask for more clarification.

ClinicMap.js

import React, { Fragment, Component } from "react";
import { Map, GoogleApiWrapper, Marker } from "google-maps-react";
import PcDialog from "../PcDialog";
import Button from "@material-ui/core/Button";
import InfoWindowEx from "./InfoWindowEx";
import { Link, Redirect } from "react-router-dom";

const mapStyles = {
  width: "100%",
  height: "100%"
};

export class ClinicMap extends Component {
  state = {
    activeMarker: {},
    selectedPlace: {
      clinic: {
        type: ""
      }
    },
    showingInfoWindow: false
  };

  onMarkerClick = (props, marker) =>
    this.setState({
      activeMarker: marker,
      selectedPlace: props,
      showingInfoWindow: true
    });

  render() {
    const { GP, PC, parentCallback } = this.props;
    const { selectedPlace } = this.state;

    const displayCurrent = (
      <Marker
        clinic={{type: "currentloc"}}
        position={{
          lat: this.props.coord[1],
          lng: this.props.coord[0]
        }}
      />
    );

    const displayGP = GP.map(clinic => {
      clinic.type = "GP";
      clinic.name = clinic.properties.HCI_NAME;
      clinic.price = "$$";
      clinic.rating = "4.3";
      clinic.doctorName = clinic.properties.DR_NAME;


      clinic.formattedOpeningHours = clinic.properties.ALL_OPENING_HOURS.map(
        period =>
          period.day_string + ":</br>" + period.opening_hours.join(",</br>")
      ).join("</br></br>");


      clinic.formattedDirections = clinic.properties.ALL_DIRECTIONS.map(
        path => path.transport_string + "</br>" + path.directions.join(",</br>")
      ).join("</br></br>");



      return (
        <Marker
          key={clinic.id}
          clinic={clinic}
          id={clinic.id}
          icon={"http://maps.google.com/mapfiles/ms/icons/green.png"}
          position={{
            lat: clinic.geometry.coordinates[1],
            lng: clinic.geometry.coordinates[0]
          }}
          onClick={this.onMarkerClick}
        />
      );
    });

    const displayPC = PC.map(clinic => {
      clinic.type = "Polyclinic";
      clinic.name = clinic.Name;
      clinic.price = "$";
      clinic.rating = "4.0";
      return (
        <Marker
          key={clinic.id}
          clinic={clinic}
          id={clinic.id}
          icon={"http://maps.google.com/mapfiles/ms/icons/blue.png"}
          position={{
            lat: clinic.coord[1],
            lng: clinic.coord[0]
          }}
          onClick={this.onMarkerClick}
        >
          <PcDialog clinic={clinic} />
        </Marker>
      );
    });
    return (
      <Map
        google={this.props.google}
        zoom={15}
        style={mapStyles}
        initialCenter={{ lat: this.props.coord[1], lng: this.props.coord[0] }}
      >
        {displayGP}
        {displayPC}
        {displayCurrent}
        {console.log(displayCurrent)}
        <InfoWindowEx
          marker={this.state.activeMarker}
          onClose={this.onInfoWindowClose}
          visible={this.state.showingInfoWindow}
          selectedPlace={selectedPlace}
        >
          {selectedPlace.clinic.type === "GP" ? (
            <div>
              GP: 
              //some other data
              <hr /> Telephone: {selectedPlace.clinic.properties.Tel} <hr />
              Applicable subsidies:{" "}
              {selectedPlace.clinic.properties.CLINIC_PROGRAMME_CODE.join(", ")}
              <hr />
              Distance:
              {parseFloat(selectedPlace.clinic.distance).toFixed(2)}km away
              <hr />


              Doctor: {selectedPlace.clinic.properties.DR_NAME}




              <hr />
              <p>Opening Hours:</p>
              <hr />
              {
                selectedPlace.clinic.properties.ALL_OPENING_HOURS.map(period => (
                  <p>
                    {period.day_string}
                    <br />
                    {period.opening_hours.join(", ")}
                  </p>
                ))
              }
              <hr />

              <p>Directions:</p>
              {
                selectedPlace.clinic.properties.ALL_DIRECTIONS.map(path => (
                  <p>
                    {path.transport_string}
                    <br />
                    {path.directions.join(", ")}
                  </p>
                ))
              }
              <hr />





              <img src={process.env.PUBLIC_URL + `/ClinicPictures/${selectedPlace.clinic.properties.FILE_NAME}.png`}
                alt="clinic picture" style={{ width: "100%" }} />

              <hr />


              <Button>
                <Link
                  to={{
                    pathname: "/ConfirmClinicChoice",
                    state: {
                      choice: selectedPlace.clinic
                    }
                  }}
                >
                  <span>Select</span>
                </Link>
              </Button>


              <Button
                variant="contained"
                color="primary"
                onClick={() =>
                  this.props.callbackFunction(selectedPlace.clinic)
                }
              >
                {console.log(selectedPlace.clinic)}
                <span style={{ color: "white" }}>Add to comparison</span>
              </Button>
            </div>
          ) : selectedPlace.clinic.type === "Polyclinic" ? (
            <div>
              //some other data
              <hr /> Telephone: {selectedPlace.clinic.Tel} <hr /> Distance:{" "}
              {parseFloat(selectedPlace.clinic.distance).toFixed(2)}km away
              <hr />


              <Button>
                <Link
                  to={{
                      pathname: "/ConfirmClinicChoice",
                    state: {
                      choice: selectedPlace.clinic
                    }
                  }}
                >
                  <span>Select</span>
                </Link>
              </Button>


              <Button
                variant="contained"
                color="primary"
                onClick={() =>
                  this.props.callbackFunction(selectedPlace.clinic)
                }
              >
                <span style={{ color: "white" }}>Add to comparison</span>
              </Button>
            </div>
          ) : (
            <div>Input Location</div>
          )}

        </InfoWindowEx>
      </Map>
    );
  }
}

export default GoogleApiWrapper({
  apiKey: ""
})(ClinicMap);

CompareDialog.js

import React, { Component, Fragment } from "react";
import Dialog from "@material-ui/core/Dialog";
import { Link } from "react-router-dom";
import Button from "@material-ui/core/Button";
import Table from "@material-ui/core/Table";
import TableBody from "@material-ui/core/TableBody";
import TableCell from "@material-ui/core/TableCell";
import TableHead from "@material-ui/core/TableHead";
import TableRow from "@material-ui/core/TableRow";
import TableFooter from "@material-ui/core/TableFooter";
import ExpandMoreIcon from "@material-ui/icons/ExpandMore";
import MyButton from "../../util/MyButton";

import {
  DialogActions,
  DialogContent,
  DialogContentText,
  DialogTitle,
  Typography
} from "@material-ui/core";
import { maxWidth, fontSize } from "@material-ui/system";
export class CompareDialog extends Component {
  state = {
    open: false,
    priceOpen: false,
    userNationality: this.props.formData.nationality,
    userAge: this.props.formData.age,
    userSubsidyType: this.props.formData.subsidyType
  };
  handleToggle = () => {
    this.setState({
      open: !this.state.open
    });
  };
  handlePriceToggle = () => {
    this.setState({
      priceOpen: !this.state.priceOpen
    });
  };
  render() {
    const {
      open,
      priceOpen,
      userNationality,
      userAge,
      userSubsidyType
    } = this.state;
    const { clinicOne, clinicTwo, formData } = this.props;
    function createData(name, gp, pc) {
      return { name, gp, pc };
    }
    const rows = [
      createData(
        <span style={{ fontWeight: "bold" }}>Name</span>,
        <span style={{ fontWeight: "bold" }}>{clinicOne.name}</span>,
        <span style={{ fontWeight: "bold" }}> {clinicTwo.name}</span>
      ),
      createData(
        "Distance",
        parseFloat(clinicOne.distance).toFixed(2),
        parseFloat(clinicTwo.distance).toFixed(2)
      ),
      createData("Price", clinicOne.price, clinicTwo.price),
      createData("Ratings", clinicOne.rating, clinicTwo.rating),
      createData("Doctor name", ((clinicOne.type === "GP") ? clinicOne.doctorName : ""),
      ((clinicTwo.type === "GP") ? clinicTwo.doctorName : "")),
      createData(
        "Opening hours",
        clinicOne.type === "GP" ? (
          <div
            dangerouslySetInnerHTML={{
              __html: clinicOne.formattedOpeningHours
            }}
          />
        ) : (
          ""
        ),
        clinicTwo.type === "GP" ? (
          <div
            dangerouslySetInnerHTML={{
              __html: clinicTwo.formattedOpeningHours
            }}
          />
        ) : (
          ""
        )
      ),

      createData(
        "Directions",
        clinicOne.type === "GP" ? (
          <div
            dangerouslySetInnerHTML={{
              __html: clinicOne.formattedDirections
            }}
          />
        ) : (
          ""
        ),
        clinicTwo.type === "GP" ? (
          <div
            dangerouslySetInnerHTML={{
              __html: clinicTwo.formattedDirections
            }}
          />
        ) : (
          ""
        )
      )

    ];

    //some data irrelevant to the problem
    const handleToggle = () => {
      this.setState({
        open: !this.state.open
      });
    };
    const handlePriceToggle = () => {
      this.setState({
        priceOpen: !this.state.priceOpen
      });
    };
    return clinicOne === null || clinicTwo === null ? (
      "Please select 2 clinics for comparison."
    ) : (
      <div>
        <Button
          variant="contained"
          style={{ backgroundColor: "#ff7c01" }}
          onClick={handleToggle}
        >
          Compare!
        </Button>
        <Dialog
          style={{ fontSize: "1vw" }}
          open={open}
          onClose={handleToggle}
          maxWidth="lg"
        >
          <DialogContent>
            <Table>
              <TableHead>
                <TableRow>
                  <TableCell> </TableCell>
                  <TableCell align="right">{clinicOne.type} </TableCell>
                  <TableCell align="right">{clinicTwo.type} </TableCell>
                </TableRow>
              </TableHead>
              <TableBody>
                {rows.map(row => (
                  <TableRow key={row.name}>
                    <TableCell component="th" scope="row">
                      {row.name === "Price" ? (
                        <Fragment>
                          Price
                          <MyButton
                            onClick={handlePriceToggle}
                            tip="More Details"
                          >
                            <Typography variant="subtitle1">Expand</Typography>
                            <ExpandMoreIcon />
                          </MyButton>
                          <Dialog open={priceOpen} onClose={handlePriceToggle}>
                            <DialogContent>
                              <p
                                style={{
                                  fontWeight: "bold",
                                  textDecoration: "underline"
                                }}
                              >
                                Cost Breakdown
                              </p>
                              <Table>
                                <TableHead>
                                  <TableRow>
                                    <TableCell />
                                    <TableCell
                                      style={{ minWidth: 200, maxWidth: 200 }}
                                      align="right"
                                    >
                                      {" "}
                                      {clinicOne.type}
                                    </TableCell>

                                    <TableCell
                                      style={{ minWidth: 200, maxWidth: 200 }}
                                      align="right"
                                    >
                                      {clinicTwo.type}
                                    </TableCell>
                                  </TableRow>
                                </TableHead>
                                <TableBody>
                                  <TableRow>
                                    <TableCell component="th" scope="row">
                                      <span style={{ fontWeight: "bolder" }}>
                                        Name
                                      </span>
                                    </TableCell>
                                    <TableCell component="th" scope="row">
                                      <span style={{ fontWeight: "bolder" }}>
                                        {clinicOne.name}
                                      </span>
                                    </TableCell>
                                    <TableCell component="th" scope="row">
                                      <span style={{ fontWeight: "bolder" }}>
                                        {" "}
                                        {clinicTwo.name}
                                      </span>
                                    </TableCell>


                                  </TableRow>
                                  {priceRows.map(row => (
                                    <TableRow key={row.name}>
                                      <TableCell component="th" scope="row">
                                        {row.name}
                                      </TableCell>
                                      <TableCell align="right">
                                        {clinicOne.type === "GP"
                                          ? row.gp
                                          : row.pc}
                                      </TableCell>
                                      <TableCell align="right">
                                        {clinicTwo.type === "GP"
                                          ? row.gp
                                          : row.pc}{" "}
                                      </TableCell>
                                    </TableRow>
                                  ))}
                                </TableBody>
                              </Table>
                            </DialogContent>
                          </Dialog>
                        </Fragment>
                      ) : (
                        <Fragment>{row.name}</Fragment>
                      )}
                    </TableCell>
                    <TableCell align="right">{row.gp}</TableCell>
                    <TableCell align="right">{row.pc} </TableCell>
                  </TableRow>
                ))}
              </TableBody>
              <TableFooter>
                <TableCell align="right">
                  <Button />
                </TableCell>


                <TableCell align="right">
                  <Button
                    variant="contained"
                    style={{ backgroundColor: "#ff7c01" }}
                  >
                    <Link
                      to={{
                        pathname: "/ConfirmClinicChoice",
                        state: {
                          choice: clinicOne,
                          formData: this.props.formData
                        }
                      }}
                    >
                      <span style={{ color: "white" }}>Select</span>
                    </Link>
                  </Button>
                </TableCell>



                <TableCell align="right">
                  <Button
                    // style={{ fontSize: "1vw" }}
                    variant="contained"
                    style={{ backgroundColor: "#ff7c01" }}
                  >
                    <Link
                      to={{
                        pathname: "/ConfirmClinicChoice",
                        state: {
                          choice: clinicTwo,
                          formData: this.props.formData
                        }
                      }}
                    >
                      <span style={{ color: "white" }}>Select</span>
                    </Link>
                  </Button>
                </TableCell>
              </TableFooter>
            </Table>
          </DialogContent>
        </Dialog>
      </div>
    );
  }
}

export default CompareDialog;

App.js

import React from "react";
import Login from "./pages/Welcome";
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
import Language from "./pages/Language";
import GeneralInfo from "./pages/GeneralInfo";
import Form from "./pages/PatientForm";
import FilteredResult from "./pages/FilteredResult";
import ConfirmClinicChoicePage from "./pages/ConfirmClinicChoice";
import confirmedChoicePage from "./pages/SummaryPage";





class App extends React.Component {
  render() {
    return (
      <Router>
        <div>
          <Switch>
            <Route path="/" exact component={Login} />
            <Route path="/Language" exact component={Language} />
            <Route path="/GeneralInfo" exact component={GeneralInfo} />
            <Route path="/Form" exact component={Form} />
            <Route path="/FilteredResult" exact component={FilteredResult} />
            <Route path="/ConfirmClinicChoice" exact component={ConfirmClinicChoicePage} />
            <Route path="/confirmedChoice" exact component={confirmedChoicePage} />
          </Switch>
        </div>
      </Router>
    );
  }
}
export default App;

package.json

{
  "name": "pathway",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@material-ui/core": "^4.1.3",
    "@material-ui/icons": "^4.2.1",
    "@turf/distance": "^6.0.1",
    "@turf/turf": "^5.1.6",
    "bootstrap": "^4.3.1",
    "bootstrap-less": "^3.3.8",
    "google-maps-react": "^2.0.2",
    "jw-react-pagination": "^1.1.0",
    "material-ui": "^0.20.2",
    "react": "^16.8.6",
    "react-bootstrap": "^1.0.0-beta.9",
    "react-dom": "^16.8.6",
    "react-js-pagination": "^3.0.2",
    "react-paginate": "^6.3.0",
    "react-router-dom": "^5.0.1",
    "react-scripts": "3.0.1",
    "react-select": "^3.0.4",
    "react-swipeable-views": "^0.13.3",
    "turf": "^3.0.14"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  },
  "devDependencies": {
    "html-loader": "^0.5.5"
  }
}

1 Answers1

0

Rendering on a map with google-maps-react is a bit limited because of how the map's children are rendered. I made a little test project and would say that google-maps-react and react-router are a bit incompatible.

If you want to dig deeper, you can use the React dev tools to see what's being rendered in terms of React components.

With google-maps-react I think the way to go is to attach click handlers, but that makes working with react-router a bit more complicated.

For maybe an easier path, you could try the google-map-react package. It works a bit differently, but makes it easy to render almost anything on the map.

Sami Hult
  • 2,869
  • 1
  • 9
  • 15