当从状态

时间:2017-11-30 16:38:09

标签: react-redux

我正在使用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本身在响应动作时似乎工作正常 - 只是状态发生了一些奇怪的事情,这会阻止其元素返回。

1 个答案:

答案 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 ........

引起循环