如果ReactJS状态具有map属性,则更改map值不会反映该状态

时间:2019-03-16 10:43:50

标签: reactjs setstate

我有一个称为“计数器”的组件

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中。 我的想法是因为地图本身从未改变,只有值改变了……因此 React认为状态从未改变!!! 。这是正确的原因。我不想使用数组。 如果我使用数组作为计数器,则代码绝对可以正常工作。

我在这里想念什么?

1 个答案:

答案 0 :(得分:2)

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

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 })
  });
}

说明:

首先,如果需要根据旧值计算状态的新值,则应使用setStatesetState(previousState => {})的签名,将当前状态作为参数传递给您。

然后,为了一成不变地更新计数器,我们首先需要克隆 counterMap:

newCounterMap = new Map(oldCounterMap);

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

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

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

请注意,对象在传播,这再次导致根据counter创建一个全新的对象(这是一种很好的做法,即使不是很必要)。

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

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

同样,请注意,我在此处使用了对象散布,以便我们既返回一个新对象,又保持其他值不变(不覆盖state的其他条目)