简体   繁体   中英

React.js, why my class component doesn't rerender the element, but functional component can work?

I'm new to react.js, just follow the tutorial. Here is my code. At first, i tried to use the class Component 'Greeting' to let it show different words after clicked the button, but i don't know what's wrong, it doesn't rerender the element, and the construtor() method of Greeting only called once. The commented out code functional Component 'Greeting' works well. Not sure what's the difference :(

class GreetingGuest extends React.Component {
    render() {
        return (
            <h3>hello Guest, Click login button !!! </h3>
        );
    }
}

class GreetingUser extends React.Component {
    render() {
        return (
            <h3>You have logged in, welcome !!!</h3>
        );
    }
}

class Greeting extends React.Component {
  constructor(props) {
      super(props);   
      console.log('Greeting.state.is_logon = ', props.is_logon);
      this.state = {is_logon: props.is_logon};
  }

  render() {
      let welcome_msg = null;
      if (this.state.is_logon) {
          welcome_msg = <GreetingUser />;                 
      }else {
          welcome_msg = <GreetingGuest />;    
      }
      return welcome_msg; 
  }
}

//function Greeting(props) {
//    const is_logon = props.is_logon;
//    if (is_logon) {
//        return <GreetingUser />;
//    }
//    return <GreetingGuest />;
//}

class LoginComponent extends React.Component {
    constructor(props) {        
        super(props);           
        this.state = {is_logon: false};
        this.handleClick = this.handleClick.bind(this);
    }

    handleClick() {
        this.setState(prevState => ({
            is_logon: !prevState.is_logon
        }));
    }

    render() {
        let button = null;
        let greeting = null;

        if (this.state.is_logon) {
            button = (
                <button onClick={this.handleClick}>Logout</button>
            );

            greeting = <Greeting is_logon={this.state.is_logon} />
        }else {
            button = (
                <button onClick={this.handleClick}>Login</button>
            );
            greeting = <Greeting is_logon={this.state.is_logon} />
        }
        return (
            <div>
                {greeting}
                {button}
            </div>
        );
    }
}

ReactDOM.render(
    <LoginComponent />,
    document.getElementById('Login')
)

HTML:

<html>
<body>
     <div id="Login"></div>
</body>
<html>

The reason the class component doesn't re render, is because you have stored the logged_in prop in state from the constructor, and the constructor is only called once. Also state can only be modified from within the component.

To fix this you have 2 options;

Use componentWillReceiveProps , and update the local state with the new logged_in prop.

componentWillReceiveProps(nextProps) {
  if (nextProps.logged_in !== this.state.logged_in) {
    this.setState({ logged_in: nextProps.logged_in });
  }
}

Or; do not use state but use the prop directly.

  render() {
      let welcome_msg = null;
      if (this.props.is_logon) {
          welcome_msg = <GreetingUser />;                 
      }else {
          welcome_msg = <GreetingGuest />;    
      }
      return welcome_msg; 
  }

Where I think you should use the latter, since the parent component already maintains state.

Well to be honest the answer which I posted previously was wrong. It was because the way you posted the question telling that everything works fine when function based component is added. Then I created a project using your code and figured out few issues in your code.

Firstly you are maintaining state locally outside redux here. You are passing down the state of the login from the parent LoginComponent to the child component called Greeting like this.

greeting = <Greeting is_logon={this.state.is_logon} />

This gets passed as a props to the child component which is Greeting in this case. Remember React uses one way data flow .

Now from that child component you can access the data using this.props as shown below. You don't need to maintain any local state what so ever there. Do the following changes in your Greeting component.

  constructor(props) {
      super(props);
  }

Then make sure you access the values from this.props instead of any local state object like this.

 render() {
      let welcome_msg = null;
      if (this.props.is_logon) {
          welcome_msg = <GreetingUser />;
      }else {
          welcome_msg = <GreetingGuest />;
      }
      return welcome_msg;
  }
}

This solved the issue. Happy Coding !

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