Javascript 无法在React服务器端渲染中处理未处理的PromisejectionWarning
我有一个React应用程序,可以在客户端和服务器上呈现。当任何组件的render()失败时(由于代码中的错误,例如试图读取未定义对象的属性),如果我从浏览器中的另一个页面导航,则会在浏览器开发人员控制台中获得完整的堆栈跟踪 但是,当我触发相同代码的服务器端渲染时(通过直接将浏览器指向包含故障组件的有问题的路由),我只会在服务器控制台中得到如下错误:Javascript 无法在React服务器端渲染中处理未处理的PromisejectionWarning,javascript,node.js,reactjs,express,Javascript,Node.js,Reactjs,Express,我有一个React应用程序,可以在客户端和服务器上呈现。当任何组件的render()失败时(由于代码中的错误,例如试图读取未定义对象的属性),如果我从浏览器中的另一个页面导航,则会在浏览器开发人员控制台中获得完整的堆栈跟踪 但是,当我触发相同代码的服务器端渲染时(通过直接将浏览器指向包含故障组件的有问题的路由),我只会在服务器控制台中得到如下错误: (节点:97192)未处理的PromisejectionWarning:未处理的承诺拒绝(拒绝id:1):TypeError:无法读取未定义的属性“
(节点:97192)未处理的PromisejectionWarning:未处理的承诺拒绝(拒绝id:1):TypeError:无法读取未定义的属性“tagDefinitionId”
由于没有堆栈跟踪,调试有点困难
问题:是否有一种方法可以在服务器端渲染期间全面捕获所有render()故障?
下面是触发服务器端渲染以响应所请求的Node Express端点的代码。我相信未处理的承诺拒绝发生在renderToString()内部,但此函数返回的是字符串,而不是承诺
`
从“../../redux/store”导入configureStore;
从'react dom/server'导入{renderToString};
从'react redux'导入{Provider};
从“React”导入React;
从“react router/lib/RouterContext”导入RouterContext;
从“react router/lib/createMemoryHistory”导入createMemoryHistory;
从“react router/lib/match”导入匹配;
从“./template”导入模板;
从“../../routes”导入路由;
const clientAssets=require(KYT.ASSETS\u清单);
应用程序使用((请求、响应)=>{
const history=createMemoryHistory(request.originalUrl);
匹配({routes,history},(错误,重定向位置,renderOps)=>{
如果(错误){
响应。状态(500)。发送(错误。消息);
}else if(重定向位置){
redirect(302,`${redirectLocation.pathname}${redirectLocation.search}`);
}else if(渲染器操作){
//这是最初的商店
const store=configureStore();
//当一个路由器路由匹配后,我们渲染
//将组件和资源添加到模板中。
常量渲染=()=>{
//从我们的Redux商店获取初始状态
const initialState=JSON.stringify(store.getState());
让isNotFoundRoute=false;
if(renderProps.routes[1]。路径==='*')){
isNotFoundRoute=true;
}
//使用当前redux存储填充HTML文档
响应。状态(isNotFoundRoute?404:200)。发送(模板({
根:renderToString(
),
cssBundle:clientAssets.main.css,
jsBundle:clientAssets.main.js,
初始状态,
}));
};
//从RenderOps中获取组件,并在组件
//承诺,在开始之前将其添加到要解决的承诺列表中
//HTML响应
获取组件数据(store.dispatch、renderProps.components、renderProps.params);
}否则{
响应状态(404).send('notfound');
}
});
});
`
我认为这里唯一相关的自定义代码导入是template.js:
`
从“react Helmet”导入头盔;
导出默认值(vo)=>{
常量头盔=头盔。倒带();
返回`
${helmet.title.toString()}
${helmet.meta.toString()}
${helmet.link.toString()}
${vo.cssBundle?'':'}
${vo.root}
窗口。\uuuu预加载\u状态\uuuu=${vo.initialState}
`;
};
`不幸的是,这听起来像是一个很难解决的问题React@15. 我能想到的唯一方法是在所有组件中的所有渲染方法中添加错误处理,这是不可行的 到目前为止,errors和React的问题是,任何错误都会使React进入不稳定状态。幸运的是,
react@next
(16)附带了componentDidCatch
——一种新的生命周期方法,可用于捕获和处理组件或任何子代(子代)中的任何错误
您可以阅读更多有关这种新行为的信息
希望这有帮助 是的,您可以使用它来记录未处理拒绝的堆栈跟踪,但您最好简单地处理它们。实际上,当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(…)
});
});
app.use((请求、响应)=>{
const history=createMemoryHistory(request.originalUrl);
新承诺((解决、拒绝)=>{
匹配({routes,history},(错误,重定向位置,renderOps)=>{
如果(错误)拒绝(错误);
else解析({redirectLocation,renderProps});
}).然后({redirectLocation,renderProps})=>{
如果(重定向位置){
返回{状态:302,目标:redirectLocation.pathname+redirectLocation.search};
}else如果(!renderProps){
返回{状态:404,内容:'未找到'};
}否则{
const store=configureStore();
返回fetchComponentData(store.dispatch、renderOps.components、renderOps.params)。然后(()=>{
const initialState=JSON.stringify(store.getState());
康斯特伊斯诺福
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>
`;
};
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(…)
});
});