Diagnosing RangeError: Maximum call stack size exceeded in React KeyEscapeUtils
Diagnosing RangeError: Maximum call stack size exceeded in React KeyEscapeUtils
背景
我们的 webapp 是使用官方 react-redux 绑定使用 React 和 Redux 编写的。此 Web 应用程序中使用的另一个主要库是 PaperJS。我们最近将其转变为 Redux 应用程序,尽管它已经使用 React 一段时间了。
问题
有时刷新(通常每隔一次刷新一次)会导致
RangeError: Maximum call stack size exceeded
at String.replace (<anonymous>)
at Object.unescape (KeyEscapeUtils.js:49)
at flattenSingleChildIntoContext (flattenChildren.js:32)
at flattenChildren.js:53
at traverseAllChildrenImpl (traverseAllChildren.js:69)
at traverseAllChildrenImpl (traverseAllChildren.js:85)
at traverseAllChildren (traverseAllChildren.js:157)
at flattenChildren (flattenChildren.js:52)
at ReactDOMComponent._reconcilerUpdateChildren (ReactMultiChild.js:209)
at ReactDOMComponent._updateChildren (ReactMultiChild.js:315)
这是失败的 React 源代码:
return ('' + keySubstring).replace(unescapeRegex, function (match) {
return unescaperLookup[match];
});
在上下文中:
/**
* Unescape and unwrap key for human-readable display
*
* @param {string} key to unescape.
* @return {string} the unescaped key.
*/
function unescape(key) {
var unescapeRegex = /(=0|=2)/g;
var unescaperLookup = {
'=0': '=',
'=2': ':'
};
var keySubstring = key[0] === '.' && key[1] === '$' ? key.substring(2) : key.substring(1);
return ('' + keySubstring).replace(unescapeRegex, function (match) {
return unescaperLookup[match];
});
}
这可能表明我在我的代码中的某个地方滥用了 React,但由于堆栈跟踪不包含对我自己的任何代码的引用,我不确定要查找什么。这似乎是重新渲染的无限循环,我怀疑这可能是由于对 setState
.
的调用不正确造成的
问题
我的怀疑有可能吗?鉴于我自己的代码库相当广泛,我该如何进一步诊断这个问题?这在 KeyEscapeUtils 中失败是什么意思?
我认为它失败的地方并不重要,因为你似乎陷入了无限循环,而这恰好是超过调用堆栈限制的地方。
为了进行诊断,我会尝试在 chrome 开发工具中打开 'Pause on Exceptions'(如果尚未打开),并在暂停时进一步查找堆栈跟踪以查找您自己的代码。
您说得对,不正确的 setState 调用可能会导致此问题。例如,如果您在 componentDidUpdate 或 render 中未选中调用 setState。奇怪的是,它只是偶尔发生。
我通过React(15.4版本)的源码搜索unescape
,只找到一处用到的地方。该文件是 react/lib/flattenChildren.js
:
function flattenSingleChildIntoContext(traverseContext, child, name, selfDebugID) {
// We found a component instance.
if (traverseContext && typeof traverseContext === 'object') {
var result = traverseContext;
var keyUnique = result[name] === undefined;
if (process.env.NODE_ENV !== 'production') {
if (!ReactComponentTreeHook) {
ReactComponentTreeHook = require('./ReactComponentTreeHook');
}
if (!keyUnique) {
process.env.NODE_ENV !== 'production' ? warning(false, 'flattenChildren(...): Encountered two children with the same key, ' + '`%s`. Child keys must be unique; when two children share a key, only ' + 'the first child will be used.%s', KeyEscapeUtils.unescape(name), ReactComponentTreeHook.getStackAddendumByID(selfDebugID)) : void 0;
}
}
if (keyUnique && child != null) {
result[name] = child;
}
}
}
它在 flattenSingleChildIntoContext
中 - 正是它在您的堆栈跟踪中的显示方式。有问题的行试图显示警告:
process.env.NODE_ENV !== 'production' ? warning(false, 'flattenChildren(...): Encountered two children with the same key, ' + '`%s`. Child keys must be unique; when two children share a key, only ' + 'the first child will be used.%s', KeyEscapeUtils.unescape(name), ReactComponentTreeHook.getStackAddendumByID(selfDebugID)) : void 0;
表示最大调用栈错误只出现在开发模式。问题是你在某处渲染两个或多个具有相同键的元素。要知道什么是关键,您可以在 Chrome Dev Tools 的这一行中使用断点。或者你可以在那里添加 console.log
并抛出异常,以便它在那里停止执行:
if (!keyUnique) {
console.log('key is not unique', name);
throw new Error('Key is not unique');
}
如果您通过这种方式获得密钥,您将能够找到具有重复密钥的元素所在的位置。
背景
我们的 webapp 是使用官方 react-redux 绑定使用 React 和 Redux 编写的。此 Web 应用程序中使用的另一个主要库是 PaperJS。我们最近将其转变为 Redux 应用程序,尽管它已经使用 React 一段时间了。
问题
有时刷新(通常每隔一次刷新一次)会导致
RangeError: Maximum call stack size exceeded
at String.replace (<anonymous>)
at Object.unescape (KeyEscapeUtils.js:49)
at flattenSingleChildIntoContext (flattenChildren.js:32)
at flattenChildren.js:53
at traverseAllChildrenImpl (traverseAllChildren.js:69)
at traverseAllChildrenImpl (traverseAllChildren.js:85)
at traverseAllChildren (traverseAllChildren.js:157)
at flattenChildren (flattenChildren.js:52)
at ReactDOMComponent._reconcilerUpdateChildren (ReactMultiChild.js:209)
at ReactDOMComponent._updateChildren (ReactMultiChild.js:315)
这是失败的 React 源代码:
return ('' + keySubstring).replace(unescapeRegex, function (match) {
return unescaperLookup[match];
});
在上下文中:
/**
* Unescape and unwrap key for human-readable display
*
* @param {string} key to unescape.
* @return {string} the unescaped key.
*/
function unescape(key) {
var unescapeRegex = /(=0|=2)/g;
var unescaperLookup = {
'=0': '=',
'=2': ':'
};
var keySubstring = key[0] === '.' && key[1] === '$' ? key.substring(2) : key.substring(1);
return ('' + keySubstring).replace(unescapeRegex, function (match) {
return unescaperLookup[match];
});
}
这可能表明我在我的代码中的某个地方滥用了 React,但由于堆栈跟踪不包含对我自己的任何代码的引用,我不确定要查找什么。这似乎是重新渲染的无限循环,我怀疑这可能是由于对 setState
.
问题
我的怀疑有可能吗?鉴于我自己的代码库相当广泛,我该如何进一步诊断这个问题?这在 KeyEscapeUtils 中失败是什么意思?
我认为它失败的地方并不重要,因为你似乎陷入了无限循环,而这恰好是超过调用堆栈限制的地方。
为了进行诊断,我会尝试在 chrome 开发工具中打开 'Pause on Exceptions'(如果尚未打开),并在暂停时进一步查找堆栈跟踪以查找您自己的代码。
您说得对,不正确的 setState 调用可能会导致此问题。例如,如果您在 componentDidUpdate 或 render 中未选中调用 setState。奇怪的是,它只是偶尔发生。
我通过React(15.4版本)的源码搜索unescape
,只找到一处用到的地方。该文件是 react/lib/flattenChildren.js
:
function flattenSingleChildIntoContext(traverseContext, child, name, selfDebugID) {
// We found a component instance.
if (traverseContext && typeof traverseContext === 'object') {
var result = traverseContext;
var keyUnique = result[name] === undefined;
if (process.env.NODE_ENV !== 'production') {
if (!ReactComponentTreeHook) {
ReactComponentTreeHook = require('./ReactComponentTreeHook');
}
if (!keyUnique) {
process.env.NODE_ENV !== 'production' ? warning(false, 'flattenChildren(...): Encountered two children with the same key, ' + '`%s`. Child keys must be unique; when two children share a key, only ' + 'the first child will be used.%s', KeyEscapeUtils.unescape(name), ReactComponentTreeHook.getStackAddendumByID(selfDebugID)) : void 0;
}
}
if (keyUnique && child != null) {
result[name] = child;
}
}
}
它在 flattenSingleChildIntoContext
中 - 正是它在您的堆栈跟踪中的显示方式。有问题的行试图显示警告:
process.env.NODE_ENV !== 'production' ? warning(false, 'flattenChildren(...): Encountered two children with the same key, ' + '`%s`. Child keys must be unique; when two children share a key, only ' + 'the first child will be used.%s', KeyEscapeUtils.unescape(name), ReactComponentTreeHook.getStackAddendumByID(selfDebugID)) : void 0;
表示最大调用栈错误只出现在开发模式。问题是你在某处渲染两个或多个具有相同键的元素。要知道什么是关键,您可以在 Chrome Dev Tools 的这一行中使用断点。或者你可以在那里添加 console.log
并抛出异常,以便它在那里停止执行:
if (!keyUnique) {
console.log('key is not unique', name);
throw new Error('Key is not unique');
}
如果您通过这种方式获得密钥,您将能够找到具有重复密钥的元素所在的位置。