简体   繁体   中英

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. How could this be fixed?

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. Therefore, the textColor property is local to the component itself, and not to the individual <td> elements.

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.

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. 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.

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/

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.

To achieve this, modify handleClick to this:

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

Edit the style rule trRed to this:

.trRed {
  color: red;
}

And finally remove the textColor: true from state since it will no longer be used.

 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>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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