2017-07-31 70 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

希望這會有所幫助!

相關問題