我正在使用react-redux 5.0.6并且有一个带有以下代码的reducer:
export default (state = [], action) => {
switch(action.type) {
case 'ADD_ENGAGEMENT':
let newArr = state.slice();
newArr.push(action.payload);
return newArr;
case 'UPDATE_ENGAGEMENT':
console.info('UPDATE_ENGAGEMENT')
return state.slice();
// return state;
default:
return state;
}}
问题发生在< UPDATE_ENGAGEMENT' case - 已删除实际逻辑并替换为最简单的示例以演示此问题。
当返回通过state.slice()从state创建的新数组时,会触发一个循环,导致调度操作,直到“Uncaught RangeError:Maximum call stack size”超过'提出错误。 Screenshot of the browser console during the issue's occurrence
问题不仅限于' slice()'并且每当返回包含任何状态元素的数组时发生,例如, return [state [0]]。 返回原始状态时,不会发生此问题。 我对这种行为感到困惑,无法理解我的应用程序中的任何内容是如何导致它的。任何见解都会受到极大的赞赏。
为了提供一些额外的背景,下面是调度行动所涉及的代码:
componentWillReceiveProps(newProps) {
let engagementTemplateData = newProps.selectedEngagementTemplate;
let engagements = newProps.engagements;
if (engagementTemplateData && engagementTemplateData.engagementUuid === this.props.uuid) {
let template = engagementTemplateData.template;
this.updateEngagementTemplate(template);
}
}
updateEngagementTemplate(template) {
let url = `/engagements/${this.props.uuid}`;
let requestHelper = new AjaxRequestHelper(url);
let data = {template_uuid: template.uuid};
this.props.updateEngagement({uuid: this.props.uuid, template: template});
// requestHelper.put(data, response => {
// this.props.updateEngagement({uuid: this.props.uuid, template: template});
// });
}
基本上,触发动作的函数在componentWillReceiveProps中作为另一个动作的结果被调用。但是,我不确定这些信息有多大帮助,因为reducer本身在响应动作时似乎工作正常 - 只是状态发生了一些奇怪的事情,这会阻止其元素返回。
答案 0 :(得分:1)
从它的声音(以及来自反应调用堆栈),我想象商店中的数组更改(通过引用)被反应组件道具拾取,在其应该/做的更新逻辑中调用该操作没有警卫。从setState
-
componentDidMount/Update
时,这通常是一个错误
当返回原始状态时它起作用,因为引用是相同的,因此React不会继续其更新逻辑,因此调用发布操作的代码
考虑这个纯粹的组件会导致你的reducer代码无限循环......
export interface IMyProps {
myArray: any[],
updateEngagementAction: () => void
}
export class EndlessLoopFromArrayPropComponent extends React.PureComponent<IMyProps> {
// PureComponent compares props by reference only
// so will cause update if this.props.myArray reference has changed in store (ie from slice())
render() {
// blahblah...
}
componentDidUpdate() {
// this will call action after every update
// as every time this action is called it passes in a new reference this.props.myArray to this component
// so react will update this component again, causing the action to be called again
// ... endless loop
this.props.updateEngagementAction()
}
}
您的实现当然会有所不同,但这将是导致它发生的原因,因此您需要在任何代码路径中添加一个保护条件,导致您的操作被调用。
对于上面的代码,您需要在发送操作或实现shouldComponentUpdate
之前检查转义条件或类似做更深的道具比较以防止不必要的更新,因此它不会到达该行为代码componentDidUpdate
方法
编辑这是在将反应代码添加到问题之前编写的。这里我引用componentDidUpdate
中没有保护的被调用的动作,但是当在由prop更改触发的任何其他生命周期方法中调用时,这同样适用,在本例中为componentWillRecieveProps
。公平地说,它确实有一个警卫,但从未返回假,因为需要进行更深入的道具检查,因此通过willreceive -> true -> action -> reducer -> willreceive -> true ........