简体   繁体   English

如何正确解决 ReactJS 中的 state 更改

[英]how to properly address a state change in ReactJS

good people.好人。

i have a small program that adds table row elements, when "Add" is clicked.当单击“添加”时,我有一个添加表格行元素的小程序。 There is also an color change option when table cell is clicked.单击表格单元格时还有一个颜色更改选项。 The problem is - when multiple elements are created, clicking on one of them, changes the color for all of them, though i have a onClick sitting only on the TD tags.问题是 - 创建多个元素时,单击其中一个元素会更改所有元素的颜色,尽管我有一个 onClick 仅位于 TD 标签上。 How could this be fixed?这怎么可能解决?

https://jsfiddle.net/mattighof/5nmcyL7b/ https://jsfiddle.net/mattighof/5nmcyL7b/

<table>
          {this.state.list.map((element, index) => {
            return (
              <tr>
                <td
                  className={this.state.textColor ? "trRed" : "trBlack"}onClick={this.handleClick}>
                  {element}
                  <div
                    onClick={e => this.removeElement(e, index)}
                    className="div"
                  />
                </td>
              </tr>
            );
          })}
        </table>

would highly appreciate your advice.非常感谢您的建议。

Since you're generating <td> elements in an anonymous function, using this inside it will refer to the parent closure, which, in this case, is the Table component.由于您在匿名 function 中生成<td>元素,因此在其中使用this将引用父闭包,在本例中,它是Table组件。 Therefore, the textColor property is local to the component itself, and not to the individual <td> elements.因此, textColor属性是组件本身的本地属性,而不是单个<td>元素的本地属性。

You're already iterating through a list that you keep in the component state, so you can slightly change the element structure to allow you to keep arbitrary state data individually.您已经在遍历您保留在组件 state 中的列表,因此您可以稍微更改元素结构以允许您单独保留任意 state 数据。

To do this, instead of adding a raw string to your list, add an object with the text and isSelected properties set to the desired values, and when rendering the <td> elements or changing colors, use the said properties.为此,不要将原始字符串添加到列表中,而是添加 object 并将textisSelected属性设置为所需的值,并在呈现<td>元素或更改 colors 时,使用上述属性。 You can, of course, name these properties to your liking, and even add more properties to individually manage the states of your elements.当然,您可以根据自己的喜好命名这些属性,甚至可以添加更多属性来单独管理元素的状态。

One other thing to note is that the current implementation of the handleClick function is unaware of the context that you're calling it from, so you'll also need to pass the index of the element when calling it, and update your state with a new list where the element at the specified index has its state updated.需要注意的另一件事是, handleClick function 的当前实现不知道您从中调用它的上下文,因此您还需要在调用它时传递元素的index ,并使用更新 state新list ,其中指定索引处的元素已更新其 state。

Here's the revised functions as per my naming:这是根据我的命名修改后的功能:

addElement() {
  this.setState({
    list: this.state.list.concat({
      text: "element",
      isSelected: false
    })
  });
}
handleClick(e, index) {
  if (!this.state.list[index]) {
    return;
  }

  // to avoid any side effects, we're taking the immutable data approach
  // and creating a new list with intended values, rather than updating the list directly
  const oldElement = this.state.list[index];
  const newElement = Object.assign({}, oldElement, {isSelected: !oldElement.isSelected});

  const newList = [].concat(this.state.list);
  newList.splice(index, 1, newElement);

  this.setState({
    list: newList
  });
}
...
  {this.state.list.map((element, index) => {
    return (
      <tr>
        <td
          className={element.isSelected ? "trRed" : "trBlack"}
          onClick={e => this.handleClick(e, index)}
        >
          {element.text}
          <div
            onClick={e => this.removeElement(e, index)}
            className="div"
          />
        </td>
      </tr>
    );
  })}
...

I've also forked your fiddle and updated it with the code blocks I mentioned above: https://jsfiddle.net/w76frtgx/我还分叉了你的小提琴并用我上面提到的代码块更新了它: https://jsfiddle.net/w76frtgx/

There are many ways to handle this.有很多方法可以处理这个问题。 Instead of relying on state to change the class, simply toggle the class trRed on the clicked target element.无需依赖 state 来更改 class,只需在单击的目标元素上切换 class trRed

To achieve this, modify handleClick to this:为此, handleClick修改为:

handleClick(e) {
  e.target.classList.toggle("trRed")
}

Edit the style rule trRed to this:将样式规则trRed编辑为:

.trRed {
  color: red;
}

And finally remove the textColor: true from state since it will no longer be used.最后从state中删除textColor: true ,因为它将不再被使用。

 class Table extends React.Component { constructor(props) { super(props); this.state = { list: [] }; this.handleClick = this.handleClick.bind(this); this.addElement = this.addElement.bind(this); this.removeElement = this.removeElement.bind(this); } handleClick(e) { e.target.classList.toggle("trRed") } addElement() { this.setState({ list: this.state.list.concat("element") }); } removeElement(e, index) { e.stopPropagation(); this.setState({ list: this.state.list.filter((_, i) => index;== i) }). } render() { return ( <div className="container"> <button onClick={this.addElement} type="button"> Add </button> <table> {this.state.list,map((element. index) => { return ( <tr> <td onClick={this.handleClick} > {element} <div onClick={e => this,removeElement(e; index)} className="div" /> </td> </tr> ); })} </table> </div> ). } } ReactDOM,render(<Table />. document;getElementById("app"));
 body { padding: 20px; } td { border: 1px solid black; height: 15px; padding: 5px; } tr { border: 1px solid black; position: relative; } table { margin-top: 10px; text-align: center; width: 70px; border: 1px solid black; background-color: beige; border-collapse: collapse; }.trRed { color: red; }.div { float: right; width: 6px; height: 6px; background-color: red; cursor: pointer; }
 <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="app"></div>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM