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"
}
}