React:如何更新数组映射中的对象

时间:2019-05-16 20:35:42

标签: javascript reactjs

我试图在单击按钮时更新数组的值。但是我不知道如何使用this.setState来做到这一点。

export default class App extends React.Component {
  state = {
    counters: [{ name: "item1", value: 0 }, { name: "item2", value: 5 }]
  };
  render() {
    return this.state.counters.map((counter, i) => {
      return (
        <div>
          {counter.name}, {counter.value}
          <button onClick={/* increment counter.value here */}>+</button>
        </div>
      );
    });
  }
}

单击按钮后如何增加counter.value

6 个答案:

答案 0 :(得分:2)

考虑将实际计数器重构为自己的组件。由于封装了组件职责,因此简化了状态管理。突然,您不需要更新嵌套的对象数组,而只需更新一个状态属性:

class App extends React.Component {
  state = {
    counters: [{ name: "item1", value: 0 }, { name: "item2", value: 5 }]
  };
  render() {
    return this.state.counters.map((counter, i) => {
      return (
        <Counter name={counter.name} count={counter.value} />
      );
    });
  }
}

class Counter extends React.Component {
  state = {
    count: this.props.count
  }

  increment = () => {
    this.setState({
      count: this.state.count+1
    })
  }

  render() {
      return (
        <div>
          {this.props.name}, {this.state.count}
          <button onClick={this.increment}>+</button>
        </div>
      );
  }
}

ReactDOM.render( < App / > , document.getElementById("root"))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>

答案 1 :(得分:0)

您可以使用已经必须将计数器映射到新数组的i,用更新后的计数替换目标项:

  () => this.setState(({ counters }) => ({ counters: counters.map((prev, i2) => i === i2 ? { ...counter, count: counter.count + 1 } : prev) }))

答案 2 :(得分:0)

您可以创建这样的点击处理程序

handleClick = (index) => {
  this.setState(state => {
    const obj = state.counters[index]; // assign the object at the index to a variable
    obj.value++; // increment the value in the object
    state.counters.splice(index, 1); // remove the object from the array
    return { counters: [...state.counters, obj] };
  });
}

这样称呼... <button onClick={() => handleClick(i)}>

可以将其缩短。只是想解释一下如何解决

答案 3 :(得分:0)

更新

因为这是现在公认的答案,所以让我在@Auskennfuchs的评论之后再给出另一种最佳方法。在这里,我们使用Object.assignindex更新当前计数器。通过这种方法,我们避免在map上使用不必要的counters

class App extends React.Component {
  state = {
    counters: [{ name: "item1", value: 0 }, { name: "item2", value: 5 }]
  };

  increment = e => {
    const {
      target: {
        dataset: { i }
      }
    } = e;
    const { counters } = this.state;
    const newCounters = Object.assign(counters, {
      ...counters,
      [i]: { ...counters[i], value: counters[i].value + 1 }
    });
    this.setState({ counters: newCounters });
  };

  render() {
    return this.state.counters.map((counter, i) => {
      return (
        <div>
          {counter.name}, {counter.value}
          {/* We are using function reference here */}
          <button data-i={i} onClick={this.increment}>
            +
          </button>
        </div>
      );
    });
  }
}

ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root" />

作为一种替代方法,您可以使用counter名称,映射到counters上并增加匹配的数字。

class App extends React.Component {
  state = {
    counters: [{ name: "item1", value: 0 }, { name: "item2", value: 5 }]
  };

  increment = name => {
    const newCounters = this.state.counters.map(counter => {
      // Does not match, so return the counter without changing.
      if (counter.name !== name) return counter;
      // Else (means match) return a new counter but change only the value
      return { ...counter, value: counter.value + 1};
    });

    this.setState({counters: newCounters});
  };


  render() {
    return this.state.counters.map((counter, i) => {
      return (
        <div>
          {counter.name}, {counter.value}
          <button onClick={() => this.increment(counter.name)}>+</button>
        </div>
      );
    });
  }
}

ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root" />

如果您使用上述处理程序,它将在每个渲染器上重新创建。您可以将其提取到单独的组件中,也可以使用数据集。

class App extends React.Component {
  state = {
    counters: [{ name: "item1", value: 0 }, { name: "item2", value: 5 }]
  };

  increment = e => {
    const {target} = e;
    const newCounters = this.state.counters.map(counter => {
      if (counter.name !== target.dataset.name) return counter;
      return { ...counter, value: counter.value + 1};
    });
    this.setState({counters: newCounters});
  };


  render() {
    return this.state.counters.map((counter, i) => {
      return (
        <div>
          {counter.name}, {counter.value}
          { /* We are using function reference here */ }
          <button data-name={counter.name} onClick={this.increment}>+</button>
        </div>
      );
    });
  }
}

ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root" />

答案 4 :(得分:0)

您可以具有一个处理程序,该处理程序将映射counters并更新所单击按钮的相应计数器项目。在这里,我们从父级作用域中选取i,并对其进行比较以找到合适的项进行更改。

<button onClick={() => {
    this.setState(state => ({
        counters: state.counters.map((item, j) => {
            // is this the counter that I want to update?
            if (j === i) {
                return {
                    ...item,
                    value: item.value + 1
                }
            }

            return item
        })
    }))
}}>+</button>

答案 5 :(得分:0)

使用Array.splice可以替换数组内的条目。这将返回一个具有替换值的新数组:

    const {counters}= this.state
    return counters.map((counter, i) => (
        <div key={i}>
          {counter.name}, {counter.value}
          <button onClick={() => this.setState({
               counters: counters.splice(i, 1, {
                   ...counter,
                   value: counter.value+1,
               }
             )})}>+</button>
        </div>
   ));

此外,最好的做法是将循环内的每个Fragment赋予自己唯一的key。您可以摆脱收益,因为map函数内部只有收益。