在React中更改tempState时如何避免副作用?

时间:2018-11-28 18:44:17

标签: javascript reactjs state setstate

因此,我花了数十个小时来思考为什么此时会发生这种情况,并且我研究了几种不同的解决方案。

尽管我已经进行了彻底的研究,但我很确定它与React的setState函数的任何生命周期方法或异步问题都没有关系。

我很确定这与tempState实际改变状态的值/引用问题无关,因为我使用lodash来深度克隆状态对象,尽管这是先前的问题。

我怀疑这与setState的浅表合并有关,但是我无法一生找出原因。

从本质上讲,我认为这段代码仅应更改typeAheadOptions数组以将hostshow的值更改为Various Shows,但它也在使状态发生变化,并且甚至没有相同的方式。状态甚至与typeAheadOptions都不一样,它的状态更深,其中还包含其他几层。

在这一点上,我可能会用Redux和ImmutableJS重建它,但是现在我真的需要了解为什么会这样,因为它使我有些疯狂。现在,我阅读了一些建议,以使您的状态对象尽可能平整,我想我现在明白为什么。

这里是仅包含相关位的摘录状态:

this.state = {
  showByName: false,
  showByShow: true,
  resources: [
    {
      show: "TED Radio Hour",
      showurl: TEDRadioHour,
      hosts: [
        {
          firstName: "Guy",
          lastName: "Raz",
          personurl: GuyRaz,
          hostshow: "TED Radio Hour"
        }
      ]
    },
    {
      show: "Radiolab",
      showurl: Radiolab,
      hosts: [
        {
          firstName: "Jad",
          lastName: "Abumrad",
          personurl: JadAbumrad,
          hostshow: "Radiolab"
        },
        {
          firstName: "Robert",
          lastName: "Krulwich",
          personurl: RobertKrulwich,
          hostshow: "Radiolab"
        }
      ]
    },
    {
      show: "How I Built This",
      showurl: HowIBuiltThis,
      hosts: [
        {
          firstName: "Guy",
          lastName: "Raz",
          personurl: GuyRaz,
          hostshow: "How I Built This"
        }
      ]
    },
    {
      show: "Radiolab Presents: More Perfect",
      showurl: RadiolabPresentsMorePerfect,
      hosts: [
        {
          firstName: "Jad",
          lastName: "Abumrad",
          personurl: JadAbumrad,
          hostshow: "Radiolab Presents: More Perfect"
        }
      ]
    }
  ],
  contentToRender: [],
  typeAheadOptions: [],
  selected: [],
  duplicateHostIndices: []
}
}

这是函数:

showByName() {
 const tempState = _.cloneDeep(this.state);
 tempState.showByName = true;
 tempState.showByShow = false;
 tempState.selected.length = 0;
 this.alphabetizeHostList(tempState);
}

alphabetizeHostList(tempState) { // sorting state so that results are alphabetical
 tempState.typeAheadOptions.length = 0;  // clear typeAhead options
 tempState.resources.forEach((resource, index) => {
   resource.hosts.forEach((host, index) => {
     tempState.typeAheadOptions.push(host);
     tempState.typeAheadOptions.sort(function(a, b) {  // sorting function
      var nameA = a.firstName.toUpperCase(); // ignore upper and lowercase
      var nameB = b.firstName.toUpperCase(); // ignore upper and lowercase
      if (nameA < nameB) {
        return -1;
      }
      if (nameA > nameB) {
        return 1;
      }
      // names must be equal
      return 0;
     });
   })
 })
 this.populateDuplicateHostIndices(tempState);
}

populateDuplicateHostIndices(tempState) { // removes duplicates by first and last name for instances like Guy Raz and Jad Abumrad
 let duplicateHostIndices = tempState.duplicateHostIndices;
 duplicateHostIndices.length = 0;
 let options = tempState.typeAheadOptions;
 let length = options.length;
 let i = 1;
 if (length > 1 && tempState.showByName === true) {
  for (i; i < length; i++) {  // iterates over the hosts and finds duplicates by first and last name
     if ((options[i - 1].firstName === options[i].firstName) && (options[i - 1].lastName === options[i].lastName)) {
      duplicateHostIndices.push(i);
     }
   }
 }
 this.removeDuplicateHosts(tempState, duplicateHostIndices);
}

 removeDuplicateHosts(tempState, duplicateHostIndices) {
  if (duplicateHostIndices.length > 0) {
   duplicateHostIndices.sort().reverse();  // if we didn't sort and reverse, we would remove the 1st host and the index of the rest would be off and we would remove them
   duplicateHostIndices.forEach((element) => {
    const previousElement = (element - 1);
    tempState.typeAheadOptions[(previousElement)].hostshow = "Various Shows";
    tempState.typeAheadOptions.splice(element, 1);
   });
 }
 this.pullContentToRenderFromTypeAheadList(tempState);
}

pullContentToRenderFromTypeAheadList(tempState) {
 tempState.contentToRender = _.cloneDeep(tempState.typeAheadOptions); // separates out content that renders from list that TypeAhead pulls from
 this.setState(tempState);
}

1 个答案:

答案 0 :(得分:0)

Reactiflux上的某位人士指出,通过使用forEach()遍历我的状态对象并运行push()以形成一个新的tempState,实际上,我实际上是在直接改变我的原始状态,而不是像我以前假设的那样是一个新的tempState

这不是setState浅合并问题,而是像我以前认为已经解决的值/引用问题。

我仅通过调用带有部分状态的setState来解决此问题,因此永远没有机会覆盖我的原始真相。

现在要对过度嵌套的状态对象进行一些重构,并将此代码重构为更加简洁。