I built a boat visualizer using specific API. The API returns a json response that I injecting it in a table.
The problem: Sometimes during the day I noticed that the application would stop working throwing an instance of:
Unhandled Rejection (TypeError): ships.reduce is not a function
Below for completeness the print screen of the error:
Below the code I am using:
const ShipTracker = ({ ships, setActiveShip }) => {
console.log("These are the ships: ", { ships });
return (
<div className="ship-tracker">
<Table className="flags-table" responsive hover>
<thead>
<tr>
<th>#</th>
<th>MMSI</th>
<th>TIMESTAMP</th>
<th>LATITUDE</th>
<th>LONGITUDE</th>
<th>COURSE</th>
<th>SPEED</th>
<th>HEADING</th>
<th>NAVSTAT</th>
<th>IMO</th>
<th>NAME</th>
<th>CALLSIGN</th>
</tr>
</thead>
<tbody>
{ships.map((ship, index) => {
// <-- Error Here
const {
MMSI,
TIMESTAMP,
LATITUDE,
LONGITUDE,
COURSE,
SPEED,
HEADING,
NAVSTAT,
IMO,
NAME,
CALLSIGN
} = ship.AIS;
const cells = [
MMSI,
TIMESTAMP,
LATITUDE,
LONGITUDE,
COURSE,
SPEED,
HEADING,
NAVSTAT,
IMO,
NAME,
CALLSIGN
];
return (
<tr
onClick={() =>
setActiveShip(
ship.AIS.NAME,
ship.AIS.LATITUDE,
ship.AIS.LONGITUDE
)
}
key={index}
>
<th scope="row">{index}</th>
{cells.map(cell => (
<td key={ship.AIS.MMSI}>{cell}</td>
))}
</tr>
);
})}
</tbody>
</Table>
</div>
);
};
Googlemap.js
class BoatMap extends Component {
constructor(props) {
super(props);
this.state = {
ships: [],
filteredShips: [],
type: "All",
shipTypes: [],
activeShipTypes: []
};
this.updateRequest = this.updateRequest.bind(this);
this.countDownInterval = null;
this.updateInterval = null;
this.map = null;
this.maps = null;
this.previousTimeStamp = null;
}
async updateRequest() {
const url = "http://localhost:3001/hello";
const fetchingData = await fetch(url);
const ships = await fetchingData.json();
console.log("fetched ships", ships);
if (JSON.stringify(ships) !== "{}") {
if (this.previousTimeStamp === null) {
this.previousTimeStamp = ships.reduce(function(obj, ship) {
obj[ship.AIS.NAME] = ship.AIS.TIMESTAMP;
return obj;
}, {});
}
this.setState({
ships: ships,
filteredShips: ships
});
this.props.callbackFromParent(ships);
for (let ship of ships) {
if (this.previousTimeStamp !== null) {
if (this.previousTimeStamp[ship.AIS.NAME] === ship.AIS.TIMESTAMP) {
this.previousTimeStamp[ship.AIS.NAME] = ship.AIS.TIMESTAMP;
console.log("Same timestamp: ", ship.AIS.NAME, ship.AIS.TIMESTAMP);
continue;
} else {
this.previousTimeStamp[ship.AIS.NAME] = ship.AIS.TIMESTAMP;
}
}
let _ship = {
// ship data ...
};
const requestOptions = {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(_ship)
};
await fetch(
"http://localhost:3001/users/vessles/map/latlng",
requestOptions
);
// console.log('Post', Date());
}
}
}
render() {
const noHoverOnShip = this.state.hoverOnActiveShip === null;
return (
<div className="google-map">
<GoogleMapReact
bootstrapURLKeys={{ key: "key" }}
center={{
lat: this.props.activeShip ? this.props.activeShip.latitude : 37.99,
lng: this.props.activeShip
? this.props.activeShip.longitude
: -97.31
}}
zoom={5.5}
onGoogleApiLoaded={({ map, maps }) => {
this.map = map;
this.maps = maps;
// we need this setState to force the first mapcontrol render
this.setState({ mapControlShouldRender: true, mapLoaded: true });
}}
>
{this.state.mapLoaded && (
<div>
<Polyline
map={this.map}
maps={this.maps}
markers={this.state.trajectoryData}
lineColor={this.state.trajectoryColor}
/>
</div>
)}
{Array.isArray(this.state.filteredShips) ? (
this.state.filteredShips.map(ship => (
<Ship
ship={ship}
key={ship.AIS.MMSI}
lat={ship.AIS.LATITUDE}
lng={ship.AIS.LONGITUDE}
logoMap={this.state.logoMap}
logoClick={this.handleMarkerClick}
logoHoverOn={this.handleMarkerHoverOnShip}
logoHoverOff={this.handleMarkerHoverOffInfoWin}
/>
))
) : (
<div />
)}
</GoogleMapReact>
</div>
);
}
}
export default class GoogleMap extends React.Component {
state = {
ships: [],
activeShipTypes: [],
activeCompanies: [],
activeShip: null,
shipFromDatabase: []
};
setActiveShip = (name, latitude, longitude) => {
this.setState({
activeShip: {
name,
latitude,
longitude
}
});
};
setShipDatabase = ships => {
this.setState({ shipFromDatabase: ships });
};
// passing data from children to parent
callbackFromParent = ships => {
this.setState({ ships });
};
render() {
return (
<MapContainer>
{/* This is the Google Map Tracking Page */}
<pre>{JSON.stringify(this.state.activeShip, null, 2)}</pre>
<BoatMap
setActiveShip={this.setActiveShip}
activeShip={this.state.activeShip}
handleDropdownChange={this.handleDropdownChange}
callbackFromParent={this.callbackFromParent}
shipFromDatabase={this.state.shipFromDatabase}
renderMyDropDown={this.state.renderMyDropDown}
// activeWindow={this.setActiveWindow}
/>
<ShipTracker
ships={this.state.ships}
setActiveShip={this.setActiveShip}
onMarkerClick={this.handleMarkerClick}
/>
</MapContainer>
);
}
}
What I have done so far:
1) I also came across this source to help me solve the problem but no luck.
2) Also I consulted this other source, and also this one but both of them did not help me to figure out what the problem might be.
3) I dug more into the problem and found this source too.
4) I read this one too. However, neither of these has helped me fix the problem.
5) I also found this source very useful but still no solution.
Thanls for pointing to the right direction for solving this problem.