【问题标题】:How to preserve state value after re-running setState重新运行 setState 后如何保留状态值
【发布时间】:2020-10-02 19:21:56
【问题描述】:

每次用户单击复选框时都会调用 setLabels,从而生成与已选择的 itemId 匹配的项目列表。 (例如,如果选中 1.3,则 for 循环会遍历 exportEntries,获取与键 1.3 匹配的所有对象值)

然后我使用 setState 将 filtersArray 设置为返回的值。但是,下次用户点击另一个复选框时,状态显然会重置。

我将如何维护所有先前选择的值并添加到状态而不是替换当前状态集?

setLabels = (e, itemId, itemLabel) => {

    let fillerArray = [];
    for (let item of this.state.exportEntries) {

      if (itemId in item) {
        fillerArray.push({ [itemLabel]: item[itemId] });
      }  

      this.setState({
        filteredArray: fillerArray
      });

    }

    console.log(this.state.filteredArray);

    e.preventDefault();
  }

第一次点击输出

0: {First: "One"}
1: {First: "Two"}
2: {First: "Three"}
3: {First: "Four"}
4: {First: "Five"}

第二次点击输出

0: {Second: "One"}
1: {Second: "Two"}
2: {Second: "Three"}
3: {Second: "Four"}
4: {Second: "Five"}

** 编辑对象数组 **

  const singleValue = [...this.state.selectedValues];

  for (let item of this.state.exportEntries) {
    if (id in item) {

      // This is pulling values from exportEntries that match the id 
      // of the 'label' selected. So 'First', 'Last', etc... all have 
      // an id that is passed to perform the check here.
      singleValue.push({ [label]: item[id] });
    }
  }

  for (var i = 0; i < singleValue.length; i++) {
    merged.push({ ...this.state.selectedValues[i], ...singleValue[i]})
  }

  // This is where I'm trying to merge the two arrays
  // If I click First & Last this is returned: 

  // (10) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
  //   0: {First: "First 1"}
  //   1: {First: "First 2"}
  //   2: {First: "First 3"}
  //   3: {First: "First 4"}
  //   4: {First: "First 5"}
  //   5: {Last: "Last 1"}
  //   6: {Last: "Last 2"}
  //   7: {Last: "Last 3"}
  //   8: {Last: "Last 4"}
  //   9: {Last: "Last 5"}

  // I want this:
  // (5) [{…}, {…}, {…}, {…}, {…}]
  //   0: {First: "First 1" , Last: "Last 1"}
  //   1: {First: "First 2", Last: "Last 2"}
  //   2: {First: "First 3", Last: "Last 3"}
  //   3: {First: "First 4", Last: "Last 4"}
  //   4: {First: "First 5", Last: "Last 5"}

【问题讨论】:

  • 您确定要在循环中每次运行setState,而不是在完成构建新数组后运行吗?
  • @Auskennfuchs 我肯定会调整结构以包含此评论。但是,我仍然需要以某种方式保留所有以前的选择。无论如何,您是否可以提供一些有关如何保留这些值的文档或示例?
  • @SethSpivey 你能分享codesandbox MVP吗?
  • @DipenShah 这一切都被捆绑成需要特定密钥的部分,并且是一个更复杂的项目的一部分,在沙箱中创建需要花费太多时间。不过,我很乐意解释或提供任何东西。
  • @SethSpivey 确定我根据自己的理解添加了建议。

标签: reactjs object setstate


【解决方案1】:

我仍然很难理解您的问题,但基于我的最佳能力,我正在尝试向您建议一些可能有效的方法。

我创建了一个POC 来解决这个问题。主要问题是您当前的状态是异步更新的,因此在更新后立即访问它不会给您更新状态。

在我的示例代码中,我添加了一堆复选框和onChange,我正在更新状态以设置所有选中的复选框,并且在setSate回调 中,我正在调用setLabels(其中我还是不明白它的作用)。

这里重要的是在状态更新后进行处理。此外,查看您的代码有两点特别突出:

  1. 您正在从一个冗余的数组内部调用 this.setState({ filteredArray: fillerArray });,而您只想调用一次。
  2. 您一直在使用新值 let fillerArray = []; 更新 FillerArray,您可能希望 let fillerArray = [...this.state.filteredArray]; 保留旧选择。

看看这个 POC 代码:

class SomeClass extends Component {
  state = {
    selectedItems: [],
    exportEntries: [],
    filteredArray: []
  };

  setLabels = (itemId, itemLabel) => {
    console.log(this.state.selectedItems);
  };

  handleCheckBoxChange = (isSelected, id, label) => {
    const newSelection = [...this.state.selectedItems];
    if (isSelected) {
      newSelection.push(label);
    } else {
      const index = newSelection.indexOf(label);
      newSelection.splice(index, 1);
    }

    this.setState({ selectedItems: newSelection }, () =>
      this.setLabels(id, label)
    );
  };

  render = () => (
    <div>
      <label>
        <input
          type="checkbox"
          onChange={(e) =>
            this.handleCheckBoxChange(e.target.checked, "name", "First")
          }
        />
        Item 1
      </label>
      <label>
        <input
          type="checkbox"
          onChange={(e) =>
            this.handleCheckBoxChange(e.target.checked, "name", "Second")
          }
        />
        Item 2
      </label>
      <label>
        <input
          type="checkbox"
          onChange={(e) =>
            this.handleCheckBoxChange(e.target.checked, "name", "Third")
          }
        />
        Item 3
      </label>
      <label>
        <input
          type="checkbox"
          onChange={(e) =>
            this.handleCheckBoxChange(e.target.checked, "name", "Fourth")
          }
        />
        Item 4
      </label>
      <div>
        {this.state.selectedItems.map((i) => (
          <p>{i}</p>
        ))}
      </div>
    </div>
  );
}

【讨论】:

  • @SethSpivey 有帮助吗?
  • 它有帮助!我目前正在努力添加一些更具体的项目,并在我完成后立即标记它!
  • 真快,你能解释一下 this.setState({ selectedItems: newSelection }, () => this.setLabels(id, label) );
  • 您可以在setState 调用中使用多个更新,以确保一次应用所有更新。
  • 看看这个:codesandbox.io/s/relaxed-cohen-cthx4,我要更新逻辑才能合并。
【解决方案2】:

据我正确理解您的要求,您希望切换复选框并保留所有选中项目的列表。您已经拥有filteredArray。在setLabelmethod 中,如果已选中,则必须添加当前项目;如果未选中,则将其从列表中删除。该事件应该为您提供此信息(e.checked 或其他内容)。

class SomeClass extends Component {
   state = {
     filteredArray: []
   }

   setLabels = (e, itemId, itemLabel) => {
     const { filteredArray } = this.state
     if(e.checked) {
       this.setState({
          filteredArray: [...filteredArray,{[itemLabel]: item[itemId]}]
       })
     } else {
        this.setState({
           filteredArray: filteredArray.filter(item => item[itemLabel]===undefined)
        })
     }
     console.log(this.state.filteredArray);
 
     e.preventDefault();
   }   
}

如果你可以检查事件中的项目,它不应该被添加到数组中,所以它会被追加。否则已添加,将被过滤掉。
过滤器看起来有点奇怪。你有itemId,我认为它是独一无二的。所以我建议,将itemId 明确添加到对象中以获得更好的过滤条件。 代码没有经过测试,但它应该以这种方式工作。

另外请记住,console.log 可能不会为您提供正确的输出,因为setState 不是同步的。

【讨论】:

  • 对不起,在执行此项目后返回为未定义在 else setState。
猜你喜欢
  • 2020-12-31
  • 2022-12-12
  • 1970-01-01
  • 1970-01-01
  • 2018-04-08
  • 1970-01-01
  • 1970-01-01
  • 2021-10-02
  • 1970-01-01
相关资源
最近更新 更多