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



所以我花了几十个小时想知道为什么会出现这种情况,我研究了一些不同的解决方案。

我确信这与React的setState函数的任何生命周期方法或异步问题无关,尽管我已经彻底研究过了。

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

我怀疑这与setState的肤浅合并有关,但我一辈子都不知道为什么。

本质上,我认为这段代码应该只更改typeAheadOptions数组,将hostshow值更改为Various Shows,但它也在改变状态,甚至不是以相同的方式。状态甚至与类型AheadOptions的形状不一样——它更深,里面有几个其他层。

在这一点上,我可能会用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);
}

Reactflux上有人善意地指出,通过用forEach((迭代我的状态对象并运行push((来形成一个新的tempState,我实际上是在直接改变我的原始状态,而不是像我之前假设的那样改变一个新tempState。

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

我通过只使用部分状态调用setState来解决这个问题,所以我的原始真相来源从来没有机会被覆盖。

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

最新更新