无法在React服务器端渲染中处理UnhandledPromiseRejectionWarning

时间:2017-07-31 11:27:01

标签: javascript node.js reactjs express

我有一个在客户端和服务器上呈现的React应用程序。当任何组件的render()失败时(由于我的代码中的错误,例如尝试读取未定义对象的属性),那么如果我从浏览器中的另一个页面导航,那么我将获得完整的堆栈跟踪浏览器开发者控制台。

但是,当我触发相同代码的服务器端呈现时(通过直接将浏览器指向包含故障组件的有问题的路由),那么我只是在服务器的控制台中得到这样的错误:

nestedlist = [] for counter in range(0,len(firstlist)): #changed line temporarylist = [] temporarylist.append(firstlist[counter]) temporarylist.append(secondlist[counter]) temporarylist.append(thirdlist[counter]) temporarylist.append(fourthlist[counter]) nestedlist.append(temporarylist)

没有堆栈跟踪,调试有点困难。

问题:有没有办法在服务器端渲染期间覆盖全部渲染()失败?

下面是触发服务器端呈现以响应所请求的Node-Express端点的代码。我相信未处理的promise拒绝发生在renderToString()中,但是这个函数返回一个字符串,而不是一个promise。

`

(node:97192) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): TypeError: Cannot read property 'tagDefinitionId' of undefined

`

我认为这里可能相关的唯一自定义代码导入是template.js:

`

import configureStore from '../../redux/store';
import { renderToString } from 'react-dom/server';
import { Provider } from 'react-redux';
import React from 'react';
import RouterContext from 'react-router/lib/RouterContext';
import createMemoryHistory from 'react-router/lib/createMemoryHistory';
import match from 'react-router/lib/match';
import template from './template';
import routes from '../../routes';

const clientAssets = require(KYT.ASSETS_MANIFEST);

app.use((request, response) => {
    const history = createMemoryHistory(request.originalUrl);

    match({ routes, history }, (error, redirectLocation, renderProps) => {
        if (error) {
            response.status(500).send(error.message);
        } else if (redirectLocation) {
            response.redirect(302, `${redirectLocation.pathname}${redirectLocation.search}`);
        } else if (renderProps) {
            // This is the initial store
            const store = configureStore();

            // When a React Router route is matched then we render
            // the components and assets into the template.
            const render = () => {
                // Grab the initial state from our Redux store
                const initialState = JSON.stringify(store.getState());

                let isNotFoundRoute = false;
                if (renderProps.routes[1].path === '*') {
                    isNotFoundRoute = true;
                }

                // Populate the HTML document with the current redux store
                response.status(isNotFoundRoute ? 404 : 200).send(template({
                    root: renderToString(
                        <Provider store={store}>
                            <RouterContext {...renderProps} />
                        </Provider>
                    ),
                    cssBundle: clientAssets.main.css,
                    jsBundle: clientAssets.main.js,
                    initialState,
                }));
            };

            // Fetch the components from the renderProps and when they have
            // promises, add them to a list of promises to resolve before starting
            // a HTML response
            fetchComponentData(store.dispatch, renderProps.components, renderProps.params).then(render);
        } else {
            response.status(404).send('Not found');
        }
    });
});

`

2 个答案:

答案 0 :(得分:1)

是的,您可以使用a catch-all来记录未处理拒绝的堆栈跟踪,但您最好简单地处理它们。实际上,当render函数中的某些内容抛出异常时,没有任何东西可以捕获它。

我建议将脚本重组为

app.use((request, response) => {
    const history = createMemoryHistory(request.originalUrl);

    new Promise((resolve, reject) => {
        match({ routes, history }, (error, redirectLocation, renderProps) => {
        if (error) reject(error);
        else resolve({redirectLocation, renderProps});
    }).then(({redirectLocation, renderProps}) => {
        if (redirectLocation) {
            return {status: 302, target: redirectLocation.pathname + redirectLocation.search};
        } else if (!renderProps) {
            return {status: 404, content: 'Not found'};
        } else {
            const store = configureStore();
            return fetchComponentData(store.dispatch, renderProps.components, renderProps.params).then(() => {
                const initialState = JSON.stringify(store.getState());
                const isNotFoundRoute = (renderProps.routes[1].path === '*');
                const content = template({
                     root: renderToString(
                        <Provider store={store}>
                            <RouterContext {...renderProps} />
                        </Provider>
                    ),
                    cssBundle: clientAssets.main.css,
                    jsBundle: clientAssets.main.js,
                    initialState,
                });
                return {status: isNotFoundRoute ? 404 : 200, content};
            });
        }
    }).catch(err => { // THIS IS IMPORTANT!
        console.error(err); // or err.message and err.stack and everything, maybe including route
        return {status: 500, content: 'Sorry, we\'ll look into it'}; // or err.message if you trust it
    }).then(({status, target, content}) => {
        if (status >= 300 && status < 400)
            response.redirect(status, target);
        else
            respose.status(status).send(content);
        if (status >= 400)
            console.debug(…)
    });
});

答案 1 :(得分:0)

不幸的是,这听起来像是在React @ 15中难以处理的问题。我能想到的唯一方法是为所有组件中的所有渲染方法添加错误处理,这是不可行的。

错误和React的问题直到现在是任何错误都会将React发送到不稳定状态。幸运的是,react@next(16)带有componentDidCatch - 一种新的生命周期方法,可以捕获和处理组件或任何后代(子)中的任何错误。

您可以阅读有关此新行为的更多信息in the React team's blog post

希望这有帮助!