【问题标题】:If ReactJS state has a map property, changing the map value doesnot reflect the state如果 React JS state 有 map 属性,改变 map 值并不会反映 state
【发布时间】:2019-03-16 10:43:50
【问题描述】:

我有一个名为 Counters 的组件

export default class Counters extends Component {
  state = {
    counterMap: {}
  };
  noOfComponents = 5;

  componentWillMount() {
    var counterMap = new Map();
    for (var i = 0; i < this.noOfComponents; i++) {
      var counter = {
        id: i + 1,
        value: i + 1,
        key: i + 1
      };
      counterMap.set(i + 1, counter);
    }
    this.setState({ counterMap });
  }

  handleDecrease = counterId => {
    // This is always Called from the inner child... Verified !
    const { counterMap } = this.state;
    const counter = counterMap.get(counterId);
    counter.value = counter.value - 1;
    counterMap.delete(counterId);
    counterMap.set(counterId, counter);
    this.setState({ counterMap: counterMap });
  };

  render() {
    const counterComps = [];
    this.state.counterMap.forEach(element => {
      const counter = (
        <Counter
          data={element}
          onDecrease={this.handleDecrease}
          onIncrease={this.handleIncrease}
        />
      );
      counterComps.push(counter);
    });
    return <div>{counterComps}</div>;
  }
}

我的问题是

handleDecrease = counterId => {
  // This is always Called
  const { counterMap } = this.state;
  const counter = counterMap.get(counterId);
  counter.value = counter.value - 1;
  counterMap.delete(counterId);
  counterMap.set(counterId, counter);
  this.setState({ counterMap: counterMap });
}

这在 UI 中不起作用。

计数器值的更改不会反映在 UI 中。 我的想法是因为地图本身从未改变,只有值改变了......所以React认为状态从未改变!!!。这是正确的理由。我不想使用数组。 如果我使用数组作为计数器,代码绝对可以正常工作。

我在这里错过了什么?

【问题讨论】:

  • 为什么不想使用数组?这似乎更适合这个用例。
  • 如果你使用setState改变状态,react就会知道它已经改变了。
  • 因为我这是一个实验。在实际场景中,我想为服务器加载大量数据[当然是延迟加载]。每个数据都将具有来自 Db 的唯一 UUID,我想创建每个 UUID 和数据本身的映射。所以我将为每个数据创建组件并根据每个组件的 id == UUID 处理交互..
  • @Amir : 谢谢你的回复,我认为 react 的状态是 map 对象的内存位置或每个键的内存....现在每个键又是一个对象...所以...没有更改内存位置..我的意思是这是我的理解...但我再次不确定...您是否能够在您的经验中使用地图?
  • 你不应该改变状态或者它的任何字段。您对setState 的使用是错误的。您应该以setState(previousState =&gt; {/*...*/}) 的形式使用它,并且您应该创建一个全新的counterMap 并替换它,不要更新它。因为状态是不可变的

标签: reactjs setstate


【解决方案1】:

您应该始终不可变地更新状态或其任何字段:

handleDecrease = counterId => {
  // This is always Called
  this.setState(prevState => {
      const { oldCounterMap } = prevState;
      newCounterMap = new Map(oldCounterMap);
      const counter = oldCounterMap.get(counterId);
      newCounterMap.set(counterId, {...counter, value: counter.value - 1});
      return Object.assign({}, prevState, { counterMap: newCounterMap })
  });
}

说明:

首先,如果你需要根据旧值计算状态的新值,那么你应该使用setState:setState(previousState =&gt; {})的这个签名,它将当前状态作为参数传递给你。

那么为了不可变地更新计数器,我们首先需要克隆 counterMap:

newCounterMap = new Map(oldCounterMap);

你可以看到这是一个克隆,因为newCounterMap === oldCounterMapfalse

然后我们继续并根据需要更新此地图:

newCounterMap.set(counterId, {...counter, value: counter.value - 1});

注意对象的传播,这再次导致基于counter 创建一个全新的对象(这只是一个好习惯,即使不是非常必要)。

最后我们返回一个全新的对象来替换我们当前的状态。

return Object.assign({}, prevState, { counterMap: newCounterMap })

再次注意,我在这里使用了对象扩展,这样我们既可以返回一个新对象,又可以保持其他值不变(不会覆盖state 的其他条目)

【讨论】:

    猜你喜欢
    • 2019-07-24
    • 1970-01-01
    • 2021-09-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-25
    相关资源
    最近更新 更多