Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ReactJS: Why are the constructor called when setState changes the state

I'm a newbie to ReactJS and I have made an app where you can submit a name and email. The name and mail should be displayed in a list at the bottom of the page. It is displayed for a short period, but then the constructor gets called and clears the state and the list.

Why is the constructor called after the state change? I thought the constructor only runs once and then the render-method runs after setState() changes the state.

class App extends React.Component {
    constructor(props) {
        super(props);

        console.log("App constructor");

        this.state = {
          signedUpPeople: []
        };

        this.signUp = this.signUp.bind(this);
    }

    signUp(person) {
        this.setState({
          signedUpPeople: this.state.signedUpPeople.concat(person)
        });
    }

    render() {
        return (
          <div>
            <SignUpForm signUp={this.signUp} />
            <SignedUpList list={this.state.signedUpPeople} />
          </div>
        );
    }
}

class SignUpForm extends React.Component {
    constructor(props) {
        super(props);

        console.log("SignUpForm constructor");

        this.state = {
          name: "",
          email: ""
        };

        this.changeValue = this.changeValue.bind(this);
        this.onSubmitForm = this.onSubmitForm.bind(this);
    }

    changeValue(event) {
        const value = event.target.value;
        const name = event.target.name;

        this.setState({
          name: value
        });
    }

    onSubmitForm() {
        this.props.signUp(this.state);
        this.setState({
          name: "",
          email: ""
        });
    }

    render() {
        console.log("SignUpForm render");
        return (
          <div>
            <h2>Sign up</h2>
            <form onSubmit={this.onSubmitForm}>
              <label htmlFor="name">Name:</label>
              <input id="name" name="name" onChange={this.changeValue} />
              <br />
              <label htmlFor="email">E-mail:</label>
              <input id="email" name="name" onChange={this.changeValue} />
              <input type="submit" value="Sign up" />
            </form>
          </div>
        );
    }
}

class SignedUpList extends React.Component {
    render() {
        console.log("SignedUpList render");
        return (
          <div>
            <h2>Already signed up</h2>
            <ul>
              {this.props.list.map(({ name, email }, index) => (
                <li key={index}>
                  {name}, {email}
                </li>
              ))}
            </ul>
          </div>
        );
    }
}

ReactDOM.render(<App />, window.document.getElementById("app"));

See CodePen example

like image 217
Lira Avatar asked Jan 07 '18 19:01

Lira


People also ask

Does setState call constructor?

setState can't be called in constructor. Because setState is async. It can result unexpected behavior if you were trying to get or modify this.

Why you should not call setState () in the constructor ()?

The constructor is executed before it is actually mounted and will not render something. That's why it makes no sense to use setState in the constructor.

What happens when state is changed in React?

React components has a built-in state object. The state object is where you store property values that belongs to the component. When the state object changes, the component re-renders.

How do you use setState in constructor in React?

Use the setState() method everywhere else; doing so accepts an object that eventually merges into the component's existing state. For example, the following does not rerender a component: // Wrong this.state.name = 'Obaseki Nosa'; Instead, use setState() .


2 Answers

The default behavior of a form with input of type submit is to post back to the server.

<input> elements of type "submit" are rendered as buttons. When the click event occurs (typically because the user clicked the button), the user agent attempts to submit the form to the server.

You can pass the event object of the submit handler and use the event.preventDefault method to prevent the form from posting back:

onSubmitForm(e) {
      e.preventDefault();
      this.props.signUp(this.state);
      this.setState({
        name: "",
        email: ""
      });
    }

Here is a running snippet of your code:

class App extends React.Component {
    constructor(props) {
      super(props);
  
      console.log("App constructor");
  
      this.state = {
        signedUpPeople: []
      };
  
      this.signUp = this.signUp.bind(this);
    }
  
    signUp(person) {
      this.setState({
        signedUpPeople: this.state.signedUpPeople.concat(person)
      });
    }
  
    render() {
      return (
        <div>
          <SignUpForm signUp={this.signUp} />
          <SignedUpList list={this.state.signedUpPeople} />
        </div>
      );
    }
  }
  
  class SignUpForm extends React.Component {
    constructor(props) {
      super(props);
  
      console.log("SignUpForm constructor");
  
      this.state = {
        name: "",
        email: ""
      };
  
      this.changeValue = this.changeValue.bind(this);
      this.onSubmitForm = this.onSubmitForm.bind(this);
    }
  
    changeValue(event) {
      const value = event.target.value;
      const name = event.target.name;
  
      this.setState({
        name: value
      });
    }
  
    onSubmitForm(e) {
      e.preventDefault();
      this.props.signUp(this.state);
      this.setState({
        name: "",
        email: ""
      });
    }
  
    render() {
      //console.log('SignUpForm render');
      return (
        <div>
          <h2>Sign up</h2>
          <form onSubmit={this.onSubmitForm}>
            <label htmlFor="name">Name:</label>
            <input id="name" name="name" onChange={this.changeValue} />
            <br />
            <label htmlFor="email">E-mail:</label>
            <input id="email" name="name" onChange={this.changeValue} />
            <input type="submit" value="Sign up" />
          </form>
        </div>
      );
    }
  }
  
  class SignedUpList extends React.Component {
    render() {
      //console.log('SignedUpList render');
      return (
        <div>
          <h2>Already signed up</h2>
          <ul>
            {this.props.list.map(({ name, email }, index) => (
              <li key={index}>
                {name}, {email}
              </li>
            ))}
          </ul>
        </div>
      );
    }
  }
  
  ReactDOM.render(<App />, window.document.getElementById("app"));
<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="app">

</div>
like image 105
Sagiv b.g Avatar answered Oct 27 '22 22:10

Sagiv b.g


onSubmitForm(e) {
    e.preventDefault(); // prevent the form from refreshing the page 

    this.props.signUp(this.state);
    this.setState({
      name: "",
      email: ""
    });
}

It is working with your codepen link :)

Have a deep look at this :

React - Preventing Form Submission

or

https://medium.com/@ericclemmons/react-event-preventdefault-78c28c950e46

like image 40
Fabien Greard Avatar answered Oct 27 '22 23:10

Fabien Greard