首页 > 解决方案 > 如果 ReactJS 的 state 有 map 属性,改变 map 的值并不会反映 state

问题描述

我有一个名为 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认为状态从未改变!.这是正确的理由。我不想使用数组。如果我使用数组作为计数器,代码绝对可以正常工作。

我在这里想念什么?

标签: reactjssetstate

解决方案


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

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 => {})它将当前状态作为参数传递给您。

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

newCounterMap = new Map(oldCounterMap);

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

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

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

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

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

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

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


推荐阅读