开发者问题收集

在递归函数中更新状态的正确方法(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