React class update

时间:2018-03-25 20:04:38

标签: javascript reactjs

Something like:

class Parent extends Component {
    render() {
        return (
        <MenuElem isSelected={false} />
        <MenuElem isSelected={true} />
        );
    }
}
class MenuElem extends Component {
    render() {
        return (
            <li onClick={() => this.setState({isSelected: true})} className={this.props.isSelected ? "is-active" : ""}>
            </li>
        );
    }
}

Why the isSelected props dont update when I fire the click event ? How can i add the "is-active" class when I click ?

3 个答案:

答案 0 :(得分:2)

You are mixing props and state together in your logic.

  • props are external parameters that a component receives from its parent. They are read only and can't be changed by the child.
  • state is a local object that gets updated. local in a way that only the very same component "knows" about it.

In your example, you are passing a Boolean isSelected from a parent to a child, then inside the child's (MenuElem) onClick event you are creating a local Boolean isSelected (inside the local state object) but when you conditionally passing the CSS style you are checking the prop isSelected passed via the parent, which will can't changed by the child as it is a read only property.

You can either check the this.state.isSelected or just pass an updated props.isSelected from the parent. The second choice will mean you need to manage the state at the parent level.

Small example for local state inside the child:

class Parent extends React.Component {
  render() {
    return (
      <MenuElem />
    );
  }
}
class MenuElem extends React.Component {

  state = {
    isSelected: false
  }

  render() {
    const { isSelected } = this.state;
    return (
      <li onClick={() => this.setState({ isSelected: true })} style={{ color: isSelected ? 'green': 'initial'  }}>
        item
      </li>
    );
  }
}

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

Example for parent state, note that it is advice to keep track for the id's so you can signal the parent which child was being clicked:

class Parent extends React.Component {

  state = {
    items: [
      { id: 1, name: 'item 1', selected: false },
      { id: 2, name: 'item 2', selected: true }]
  }

  onItemClick = itemId => {
    this.setState(prev => {
      const nextItems = prev.items.map(item => {
        return {
          ...item,
          selected: item.id === itemId
        }
      });
      return { items: nextItems };
    })
  }

  render() {
    const { items } = this.state;
    return (
      <div>
        {
          items.map(item => (
            <MenuElem
              key={item.id}
              {...item}
              onClick={this.onItemClick}
            />
          ))
        }
      </div>
    );
  }
}
class MenuElem extends React.Component {

  onClick = () => {
    const { onClick, id } = this.props;
    onClick(id);
  }

  render() {
    const { selected, name } = this.props;
    return (
      <li
        onClick={this.onClick}
        style={{ color: selected ? 'green' : 'initial' }}>
        {name}
      </li>
    );
  }
}

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

答案 1 :(得分:1)

props and state are different things. Your MenuElem component know about isSelected as props, not state. So no setState for the guy. Two options:

1) isSelected is in state in Parent component and you pass it as props to MenuElem among a callback to do the setState (but the state belongs to Parent)

2) Parent stays the same and you assign isSelected to state for MenuElem (then you can do a setState, and in this case the state is in MenuElem)

答案 2 :(得分:1)

You can fix your script by making sure MenuElem is statefull, in this way you can set its own internal state based on your event (onClick for instance).

Example:

class MenuElem extends React.Component {

  state = {
    isSelected: false
  }

  render() {
    return (
      <li onClick={() => this.setState({ isSelected: true })} style={{ color: isSelected ? 'green': 'initial'  }}>
        item
      </li>
    );
  }
}

Or if you need only props you can do

class MenuElem extends React.Component {
  render() {
    return (
      const { isSelected } = this.props
      <li onClick={() => this.setState({ isSelected })} style={{ color: isSelected ? 'green': 'initial'  }}>
        item
      </li>
    );
  }
}