简体   繁体   中英

Element created in constructor not updated when component rerenders

Why is the <select> I create in the constructor not updated when I pick another flavor in it? The other select , as well as the text, is updated.

 class ConstructorComponent extends React.Component { constructor() { super(); this.state = { icecream: 'vanilla', }; this.select = ( <select value={this.state.icecream} onChange={this.onChange} > <option value="chocolate">Chocolate</option> <option value="vanilla">Vanilla</option> <option value="strawberry">Strawberry</option> </select> ); } onChange = event => { this.setState({ icecream: event.target.value, }); }; render() { return ( <div> Icecream flavor: {this.state.icecream} <br /> {this.select} <br /> <select value={this.state.icecream} onChange={this.onChange} > <option value="chocolate">Chocolate</option> <option value="vanilla">Vanilla</option> <option value="strawberry">Strawberry</option> </select> </div> ); } } ReactDOM.render( <ConstructorComponent />, document.getElementById('container') ); 
 <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="container"> <!-- This element's contents will be replaced with your component. --> </div> 

I made it work by cloning this.select in render() like so: {React.cloneElement(this.select, {value: this.state.icecream})}

But making this.select a method instead, as suggested by several answers to my question is probably better. I'll see what works best with my actual code, and not just this dumbed down example :)

Because this.state.icecream in your first select is interpreted only once, when you define this.select in the constructor. So when your ConstructorComponent rerenders a second time when its state changes, there is nothing to update in your first select.

You need to define this.select as a function returning the <select> , like this:

this.select = () => {
  return (
    <select
      value={this.state.icecream}
      onChange={this.onChange}
    >
      <option value="chocolate">Chocolate</option>
      <option value="vanilla">Vanilla</option>
      <option value="strawberry">Strawberry</option>
    </select>
  )
}

And call this.select() in the render.

You can also create a reference to the <select> in the render by using the prop ref :

render() {
  <select
    value={this.state.icecream}
    onChange={this.onChange}
    ref={select => (this.select = select)}  // the magic happens here
  >
    <option value="chocolate">Chocolate</option>
    <option value="vanilla">Vanilla</option>
    <option value="strawberry">Strawberry</option>
  </select>
}

In doing so, you don't need to define this.select as a function in your constructor.

The first select doesn't work because it's only ever rendered once, in the constructor. The second select works because it's re-rendered every time the app state updates.

If it helps, don't think of JSX elements as "instances" of actual HTML elements. Think of them as a simple means of how you want your app to look in relation to your state. If my state is "a", then I render a select with "a", and if my state is "b", then I render a select with "b", and so on.

The first select box on the page is only rendered in the constructor which is called just once, thus the component will always remain the same as it does when it is first rendered. In other words it is static. The second select box is going to be re-rendered each time the component is updated.

In order to fix your code you can change your line in the constructor to a function:

this.select = () => (
  <select
    value={this.state.icecream}
    onChange={this.onChange}
  >
    <option value="chocolate">Chocolate</option>
    <option value="vanilla">Vanilla</option>
    <option value="strawberry">Strawberry</option>
  </select>
);

Then change your return statement to call this function:

<br />
  {this.select()}
<br />

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