2017-07-31 207 views
1

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

然而,当我触发一个服务器端渲染相同的代码(直接将浏览器指向包含有故障的部件有问题的路线),然后我就得到这样的错误,这些在服务器的控制台:

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

不带堆栈跟踪,这是一个有点难以调试。

问题:有没有办法来毯包罗万象的渲染()失败时的服务器端渲染?

触发服务器端响应于被请求的节点Express端点呈现的代码如下。我相信在renderToString()内发生未处理的承诺拒绝,但是这个函数返回一个字符串,而不是一个承诺。

`

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'); 
     } 
    }); 
}); 

`

我想自定义代码可能与此有关的唯一进口是template.js:

`

import Helmet from 'react-helmet'; 

export default (vo) => { 
    const helmet = Helmet.rewind(); 
    return ` 
     <!DOCTYPE html> 
     <html lang="en" ${helmet.htmlAttributes.toString()} > 
      <head> 
       ${helmet.title.toString()} 
       <meta httpEquiv="X-UA-Compatible" content="IE=edge" /> 
       <meta charSet='utf-8' /> 
       <meta httpEquiv="Content-Language" content="en" /> 
       <meta name="viewport" content="width=device-width, initial-scale=1" /> 
       <meta name="theme-color" content="white" /> 
       ${helmet.meta.toString()} 
       ${helmet.link.toString()} 
       <link id="favicon" rel="shortcut icon" href="/favicon.png" sizes="16x16 32x32" type="image/png" /> 
       <link rel="manifest" href="manifest.json"> 
       ${vo.cssBundle ? '<link rel="stylesheet" type="text/css" href="' + vo.cssBundle + '">' : ''} 
      </head> 
      <body ${helmet.bodyAttributes.toString()} > 
       <div id="root" class="root"><div>${vo.root}</div></div> 
       <script> 
        window.__PRELOADED_STATE__ = ${vo.initialState} 
       </script> 
       <script async src="${vo.jsBundle}"></script> 
      </body> 
     </html> 
    `; 
}; 

`

+0

如何只处理错误,而不是让它未处理? '.then(render).catch(err => {console.error(err); response.status(500).send(“sorry,we're looking into it”)})? – Bergi

+0

是的,[还有一个全面的](https://nodejs.org/api/process.html#process_event_unhandledrejection),你可以用它来记录你的堆栈痕迹 – Bergi

+0

...我以为我试过没有成功...但你的第一个建议工作得很好。你可以提交这个答案,我可以接受吗? – womblbombl

回答

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(…) 
    }); 
}); 
0

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

与错误的问题并作出反应,直至现在的任何错误都会发送反应生成不稳定的状态。幸运的是,[email protected](16)附带componentDidCatch - 一种新的生命周期方法,可让您捕捉并处理组件或任何后代(儿童)中的任何错误。

你可以阅读更多关于这个新的行为in the React team's blog post

希望这会有所帮助!

相关问题