title: How can I avoid side-affects when mutating tempState in React?
body: <p>So I’ve spent dozens of hours wondering why this is going on at this point, and I’ve looked into a few different solutions.</p>
  
  <p>I’m pretty sure it doesn’t have to do with any lifecycle methods or asynchronous issues of React’s setState function, though I've investigated thoroughly.</p>
  
  <p>I’m pretty sure it doesn’t have to do with value/reference issues of tempState actually mutating state, because I’m using lodash to deepClone the state object, though this was a previous issue.</p>
  
  <p>I suspect it has to do with setState’s shallow merge, but I cannot for the life of me figure out why.</p>
  
  <p>Essentially, I thought that this code should only change the typeAheadOptions array to change the <code>hostshow</code> values to <code>Various Shows</code>, but it is mutating the state as well, and not even in the same way. The state isn't even the same shape as the the typeAheadOptions-- it is deeper with a couple other layers in it.</p>
  
  <p>At this point, I might rebuild this with Redux and ImmutableJS, but now I really need to understand why this is happening, because it is making me slightly crazy. And now I've read suggestions to keep your state objects as flat as possible, and I think I understand why now. </p>
  
  <p>Here’s the snipped state with just the relevant bits:</p>
  
  <pre><code>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: []
  }
  }
  </code></pre>
  
  <p>And here are the functions:</p>
  
  <pre><code>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) =&gt; {
     resource.hosts.forEach((host, index) =&gt; {
       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 &lt; nameB) {
          return -1;
        }
        if (nameA &gt; 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 &gt; 1 &amp;&amp; tempState.showByName === true) {
    for (i; i &lt; length; i++) {  // iterates over the hosts and finds duplicates by first and last name
       if ((options[i - 1].firstName === options[i].firstName) &amp;&amp; (options[i - 1].lastName === options[i].lastName)) {
        duplicateHostIndices.push(i);
       }
     }
   }
   this.removeDuplicateHosts(tempState, duplicateHostIndices);
  }
  
   removeDuplicateHosts(tempState, duplicateHostIndices) {
    if (duplicateHostIndices.length &gt; 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) =&gt; {
      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);
  }
  </code></pre>