【问题标题】:ReactJS: Unhandled Rejection (TypeError) 304ReactJS:未处理的拒绝(TypeError)304
【发布时间】:2020-06-16 14:40:14
【问题描述】:

我使用this API 构建了一个船可视化器。查询 API 后,我可以获取我感兴趣的船只的 json 响应,并将这些 API 信息写入MongoDB 数据库。 API请求可以每1分钟完成一次,这就是我使用const NodeCache = require('node-cache');模块绕过1分钟限制的原因。

问题: 一切似乎都运行良好,但如果我手动刷新页面以查看船只的更新位置并在 1 分钟前发送请求,我会得到 304 of @ 987654333@。所以程序不会崩溃,而是不断跳过 1 分钟将信息写入MongoDB。这意味着由于刷新页面操作而不是每 1 分钟,我每 2 分钟获得一次职位。为什么会这样?

UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)

API 的典型答案如下:

[  
    {  
        "AIS":{  
            "MMSI":227441980,
            "TIMESTAMP":"2017-08-11 11:17:37 UTC",
            "LATITUDE":46.1459,
            "LONGITUDE":-1.16631,
            "COURSE":360.0,
            "SPEED":0.0,
            "HEADING":511,
            "NAVSTAT":1,            
            "IMO":0,
            "NAME":"CLEMENTINE",
            "CALLSIGN":"FJVK",
            "TYPE":60,
            "A":0,
            "B":0,
            "C":0,
            "D":0,
            "DRAUGHT":0.0,
            "DESTINATION":"",
            "ETA_AIS":"00-00 00:00",
            "ETA":"",
            "SRC":"TER",
            "ZONE": "North Sea",
            "ECA": true      
        }
    }
]

下面是代码最重要的部分:

服务器

var express = require('express');
var router = express.Router();
var axios = require('axios');
const NodeCache = require('node-cache');
const myCache = new NodeCache();

let hitCount = 0;

/* GET home page. */
router.get('/', function(req, res, next) {
    res.render('index', { title: 'Express' });
});

router.get('/hello', async function(req, res, next) {
    const allData = myCache.get('allData');

    if (!allData) {
        hitCount++;
        try {
            const { data } = await axios.get(
                'https://api.vesselfinder.com/vesselslist?userkey=KEY'
            );
            const { metaData, ships } = data;
            myCache.set('allData', data, 70);
            console.log(data + 'This is the data');
            res.send(data);
        } catch (error) {
            res.send(error);
            console.log(error);
        }
    }
    res.send(allData);
});

module.exports = router;

客户

class BoatMap extends Component {
    constructor(props) {
        super(props);
        this.state = {
            // .........  
        };
        this.updateRequest = this.updateRequest.bind(this);
    }

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

        await this.updateRequest();

        this.updateInterval = setInterval(() => {
            this.updateRequest();
        }, 60 * 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) {   
                    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 data ....
                };
                const requestOptions = {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify(_ship)
                };
                await fetch('http://localhost:3001/users/vessles/map/latlng', requestOptions);
            }
        }
    }
}





render() {
    const noHoverOnShip = this.state.hoverOnActiveShip === null;
    // console.log("color", this.state.trajectoryColor);
    return (
        <div className="google-map">
            <GoogleMapReact
                // ships={this.state.ships}
                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>
                ) : (
                    ''
                )}

                {/* Rendering all the markers here */}
                {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}
                    />
                ))}

到目前为止我做了什么:

1) 我也遇到了this source 来帮助我解决问题,但没有运气。

2) 我还咨询了this other source 和this one,但他们都没有帮助我找出问题所在。

3) 我更深入地研究了这个问题,发现了this source too。

4) 我读到了this one too。但是,这些都没有帮助我解决问题。

5) 我还发现this source 非常有用,但仍然没有解决方案。

非常感谢您指出解决此问题的正确方向。

【问题讨论】:

  • 您应该在您的setInterval 中捕获来自this.updateRequest() 的错误;错误应该包含一个调用堆栈,以便您知道错误在哪一行。
  • @Jacob,感谢您的建议。您能否将您的答案与一些代码整合起来,以便我理解您的建议? :)
  • 在我们知道实际错误是什么之前,我们仍然不知道如何回答您的问题。获取更多数据的一种简单方法是将this.updateRequest() 替换为this.updateRequest().catch(console.error),这样错误就不再是未处理的拒绝,然后查看错误控制台以查看错误是什么以及行号是什么。或者您的控制台中可能已经有了它,您可以添加到问题中。
  • 没关系,我在答案的 cmets 中看到错误是 this.state.filteredShips.map is not a function,但您发布的代码没有该行。您是否缺少引发错误的代码?
  • 如果有用的话,我用打印屏幕更新了这个问题。

标签: javascript node.js reactjs


【解决方案1】:

正如错误所说,this.state.filteredShips.map 在某些情况下不是一个函数。这可能意味着您忽略的初始状态没有this.state.filteredShips 作为Array。如果您还没有过滤船只,那没关系,但您的渲染代码必须考虑到您的道具和状态的所有可能性。如果您正在调用this.state.filteredShips.map,而这不是一个函数,那么您的渲染将会失败。您应该检查 this.state.filteredShips 的值,并在尚未填充的情况下呈现其他内容,例如:

{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}
    />
  ))
  : 'Loading...'
}

【讨论】:

  • 嗨,Jacob,感谢您的帮助 :)。我试过了,现在它的工作时间要长得多。我看到你说“考虑到所有可能性”。 :)
  • 我仍然有一个奇怪的错误触发,想知道你是否有时间看看? question is here。谢谢你昨天的时间!非常感谢! :)
【解决方案2】:

我知道你想要状态 cmd:

       this.setState({
            ships: ships,
            filteredShips: ships
        });

在去之前完成

for (let ship of ships) {...

如果是这种情况你需要使用 setState 回调:

           this.setState({ ..... }, () => {do what you need next})

setState 是异步的,除非您使用回调,否则无法保证状态变量 ship 和 filteredShips 在进入循环之前会获得您需要的值。

另一方面,我从不使用 await 来调用服务器。如果我使用 Axios,我倾向于使用它的回调。更多信息:

https://www.npmjs.com/package/react-axios

希望这会有所帮助。

【讨论】:

  • 感谢您访问并阅读问题。我明白你在说什么,这实际上解释了为什么我得到TypeError: this.state.filteredShips.map is not a function
  • 您能否将您的答案与我发布的代码结合起来,以便我完全理解您的建议?
  • 我建议您首先使 setState 根据需要工作。创建一个函数,该函数将在 setState 完成后运行您需要执行的代码,并从 setState 回调中调用此函数。你用的是 fetch 但我用的是 Axios。
猜你喜欢
  • 2017-11-23
  • 1970-01-01
  • 1970-01-01
  • 2019-03-19
  • 1970-01-01
  • 2022-01-03
  • 2023-04-06
  • 1970-01-01
  • 2021-01-06
相关资源
最近更新 更多