在递归函数中更新状态的正确方法(reactjs)
2020-07-07
2512
我正在 React 中开发一款应用,帮助刚接触特定模式的用户以交互方式创建查询。我目前正在开发的功能如下:任何时候从查询中删除关系,从该关系访问的字段也应被删除。假设某些用户可能会运行几层深度的查询,列表顶部的删除将删除大量子项,这些子项可能还有自己的子项。因此,我递归调用删除函数,检查子项,首先对其子项调用删除,等等,直到我们完全返回。该函数有效并命中所有正确的节点(我可以通过控制台日志验证这一点),我遇到的问题是由于 setState 是异步的,状态仅在最后一次调用时更新,并且实际上只有顶部节点才会从列表中过滤掉。我使用的代码是:
cascadeDeleteQueryFields(id) {
//loop through the array checking for anyone with parent of id^
this.state.queryFields.forEach((item) => {
if (item.parent === id) {
console.log("calling for item " + item.id);
this.cascadeDeleteQueryFields(item.id);
}
});
console.log("filtering ID : " + id);
const queryFields = this.state.queryFields.filter((c) => c.id !== id);
this.setState({ queryFields });
}
(当前登录仅用于调试目的)
有更多 React 经验的人可以推荐一种更好的方法来更新我的状态,以便捕获递归调用中的每个更改吗?我查看了其他问题,但它们都不像我的一样在递归函数中,因此解决方案似乎无法正常工作或效率极低。
1个回答
这是我在评论中建议的方法。
descendants
是一个纯函数,给定一个集合和一个 id,返回该 id 以及具有该 id 的元素的(递归)后代的 id,如某些对象的
parentId
字段所示。
deleteQueryFields
方法调用
descendants
一次,然后调用
setState
一次,结果是过滤结果中未包含的节点。
const descendants = (xs) => (id) => [
id,
... xs .filter (({parentId}) => parentId == id)
.map (o => o .id)
.flatMap (descendants (xs))
]
class FakeReactComponent {
constructor (state) {
this .state = state
}
setState (newState) {
console.log ('setState called') // should only happen once
this .state = Object .assign ({}, this .state, newState)
}
deleteQueryFields (id) {
const toRemove = descendants (this .state .queryFields) (id)
this.setState ({
queryFields: this .state .queryFields .filter (({id}) => !toRemove .includes (id))
})
}
}
const component = new FakeReactComponent ({
queryFields: [{id: 1}, {id: 2}, {id: 3, parentId: 2}, {id: 4, parentId: 2},
{id: 5, parentId: 1}, {id: 6, parentId: 5}, {id: 7, parentId: 6},
{id: 8}, {id: 9, parentId: 4}, {id: 10, parentId: 8}]
})
component .deleteQueryFields (2)
console.log (component.state)
.as-console-wrapper {min-height: 100% !important; top: 0}
您应该看到
setState
仅被调用一次,但 id 为 2 的元素已连同其所有后代一起被删除。
您说您是 JS 新手。如果任何语法令人困惑,请随时询问。
Scott Sauyet
2020-07-07