0

I built a boat visualizer using specific API. After inquiring the API I am able to obtain a json response with the vessels I am interested in and write those API information into a MongoDB database and show the vessels icons on a Google Map. The API has a 1 minute query limit, therefore to bypass the problem I set an automatic counter to 1 minute and 5 seconds and after that I launch the request.

The problem: The applications works great but if I manually refresh the page too many times within 1 minute I get the following error:

Unhandled Rejection (TypeError): ships.reduce is not a function

Below a print screen of the error:

cbox

Below the code:

class BoatMap extends Component {
    constructor(props) {
        super(props);
        this.state = {
            ships: [],
            filteredShips: [],
            type: 'All',
            shipTypes: [],
            activeShipTypes: [],
            mapControlShouldRender: false,
            mapLoaded: false
        };
        this.updateRequest = this.updateRequest.bind(this);
        this.countDownInterval = null;
        this.updateInterval = null;
        this.map = null;
        this.maps = null;
        this.previousTimeStamp = null;
    }

// Timed request
async componentDidMount() {
    this.countDownInterval = setInterval(() => {}, 500);

    await this.updateRequest();

    this.updateInterval = setInterval(() => {
        this.updateRequest();
        // Operate automatic request every 1 minute and 5 seconds
    }, 65 * 1000);
}


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) { // <-- Error Here
                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 = {
                // ships info .....
            };
            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());
        }
    }
}

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.

Thank you very much for pointing to the right direction for solving this issue.

Emanuele
  • 2,194
  • 6
  • 32
  • 71
  • 2
    Is the returned ```ships``` an ```Array``` ? Because the error says just that, that ```ships is not an array```, reduce is for arrays only. [Documentation here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce) – CevaComic Jun 18 '20 at 18:39
  • hello and thanks for stopping by and reading the question. Yes `ships` is an array. The API returns an array of `ships`. – Emanuele Jun 18 '20 at 18:40
  • 1
    I agree! Maybe it makes sense to validate your response with `typeof ships === 'object'` instead of `JSON.stringify(ships) !== '{}'`? – sebastian-ruehmann Jun 18 '20 at 18:41
  • JSON.stringify(array) would not === '{}'. Something is wrong with the type of data in ships – user120242 Jun 18 '20 at 18:54
  • @user120242, thanks for stopping by and read the question. Basically with that statement I was saying that if time stamp is different from previous time stamp, give it to me, if it is the same don't save it. – Emanuele Jun 18 '20 at 20:04
  • What is that I am doing wrong? Could you please answer with some code so that I can fully understand your suggestion? – Emanuele Jun 18 '20 at 20:04
  • you only check if previousTimestamp is null there though? You aren't comparing any values there. Also that reduce statement will never return null, is that what you were trying to do? You don't filter any of the timestamps either – user120242 Jun 18 '20 at 20:07
  • "The API has a 1 minute query limit, therefore to bypass the problem I set an automatic counter to 1 minute and 5 seconds and after that I launch the request." - this is your **problem**, if there's a limit, you don't always get the desired response back, but rather an error or some other message. And you're still not handling this case properly in your `updateRequest` method. – goto Jun 19 '20 at 12:05
  • @goto1, yes and in fact I am taking care of that applying [your suggestion](https://stackoverflow.com/questions/62439549/unhandled-rejection-typeerror-ships-reduce-is-not-a-function) but I have bit of problems merging your code with mine. I will get back to you shortly. – Emanuele Jun 19 '20 at 13:55

1 Answers1

1

It seems that the response from the API is not Array. Probably, due to query limit, it returns something else and JSON.stringify(ships) !== "{}" is not a bullet-proof check for Array response. Try this check, which guaranties that you always run reduce function against the array:

...
  async updateRequest() {
    const url = "http://localhost:3001/hello";
    const fetchingData = await fetch(url);
    const ships = await fetchingData.json();
    console.log("fetched ships", ships);

    if (Array.isArray(ships)) { // the new check for array response
      if (this.previousTimeStamp === null) {
        this.previousTimeStamp = ships.reduce(function (obj, ship) {
          obj[ship.AIS.NAME] = ship.AIS.TIMESTAMP;
          return obj;
        }, {});
      }
...
Rostyslav
  • 2,606
  • 9
  • 17
  • Hi and thanks for reading the question. I will give it a try today and get back to you. In the meantime thanks for your time! :) – Emanuele Jun 19 '20 at 11:21