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.
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.
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.
I suspect it has to do with setState’s shallow merge, but I cannot for the life of me figure out why.
Essentially, I thought that this code should only change the typeAheadOptions array to change the hostshow
values to Various Shows
, 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.
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.
Here’s the snipped state with just the relevant bits:
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: []
}
}
And here are the functions:
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);
}