简体   繁体   中英

Class Component ReactJS - setTimeout get wrong value in class component

App Component:

class App extends React.Component {
        constructor() {
          super();
            this.state = {
            user: 'Dan',
          };
        }
        render() {
          return (
            <React.Fragment>
              <label>
                <b>Choose profile to view: </b>
                <select
                  value={this.state.user}
                  onChange={e => this.setState({ user: e.target.value })}
                >
                  <option value="Dan">Dan</option>
                  <option value="Sophie">Sophie</option>
                  <option value="Sunil">Sunil</option>
                </select>
              </label>
              <h1>Welcome to {this.state.user}’s profile!</h1>
              <p>
                <ProfilePageClass user={this.state.user} />
                <b> (class)</b>
              </p>
            </React.Fragment>
          )
        }
      }

ProfilePageClass ( the problem is here ):

class ProfilePageClass extends React.Component {
  showMessage = () => {
    alert('Followed ' + this.props.user); // This get wrong value (new props)
  };

  handleClick = () => {
    setTimeout(this.showMessage, 6000); // This get wrong value (new props)
  };

  render() {
    return <button onClick={this.handleClick}>Follow</button>;
  }
}

setTimeout does not display the message corresponding to the user that was originally followed

I think it's a problem with the props or this , but I'm not sure.

Can anyone tell me what is going on?

Nothing to do with the this keyword. When the app state changes, your component instance receives new props values, and the setTimeout callback that runs after they have changed will access the new values. This is sometimes desirable, sometimes not.

This is one of the differences between function components and class components . To get the user profile that was rendered when you clicked the button, you need to explicitly remember it when the button is clicked (or rendered):

class ProfilePageClass extends React.Component {    
  handleClick = () => {
    const user = this.props.user;
//  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    setTimeout(() => {
      alert('Followed ' + this.props.user); // current value (wrong)
      alert('Followed ' + user); // old value (expected)
    }, 6000);
  };

  render() {
    return <button onClick={this.handleClick}>Follow</button>;
  }
}

With a function component, you can't get it wrong (but accessing the current value is next to impossible without useRef ):

function ProfilePageClass({user}) {    
  const handleClick = () => {
    setTimeout(() => {
      alert('Followed ' + user); // old value (expected)
    }, 6000);
  };
  return <button onClick={this.handleClick}>Follow</button>;
}

It's likely there's a problem regarding the setTimeout your using, in which you're losing reference to the actual prop you wish to display. You can add something like this to your parent component:

this.myRef = React.createRef();

This will generate a ref that you can later pass in to the child component. You can set the refs current item in this fashion:

this.myRef.current = this.state.user

in order to populate the ref.

Try this modification, as there might be handleClick is not auto bound.

import React from "react";
export default class App extends React.Component {

  constructor(props) {
    super(props);
      this.state = {
      user: 'Dan',
    };
  }
  render() {
    return (
      <React.Fragment>
        <label>
          <b>Choose profile to view: </b>
          <select
            value={this.state.user}
            onChange={e => this.setState({ user: e.target.value })}
          >
            <option value="Dan">Dan</option>
            <option value="Sophie">Sophie</option>
            <option value="Sunil">Sunil</option>
          </select>
        </label>
        <h1>Welcome to {this.state.user}’s profile!</h1>
        <p>
          <ProfilePageClass user={this.state.user} />
          <b> (class)</b>
        </p>
      </React.Fragment>
    )
  }
}

Profile alert

class ProfilePageClass extends React.Component {
//Define a constructor here
  constructor(props){
    super(props)
// Bind handleClick , so that `this` will point to parent component when you pass to child
    this.handleClick= this.handleClick.bind();
  }
  showMessage = () => {
    alert('Followed ' + this.props.user); // This get value (new props)
  };

  handleClick = () => {
    setTimeout(this.showMessage, 100); // This get  value (new props)
  };

  render() {
    return <button onClick={this.handleClick}>Follow</button>;
  }
}

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