简体   繁体   中英

Passing state after .setState() to child component but not showing up in this.props for child component

How come I am able to print my this.state.email after running componentDidMount() but when I pass the state to a child component I cannot access it in this.props.email of the child component

class HomePage extends React.Component {
  constructor(props, context) {
    super(props, context);
    this.state = {
      isAuthorized: false,
      name: "",
      email: "",
    };
  }

  loadGoogleApi() {
    ...
    this.makeApiCall()
    ...
  }

  makeApiCall() {
    gapi.client.people.people.get({
      'resourceName': 'people/me',
      'personFields': 'names,emailAddresses',
    }).then(resp => {
      this.setState({
        name: resp.result.names[0].givenName,
        email: resp.result.emailAddresses[0].value
      });
    });
  }

  componentDidMount() {
    this.loadGoogleApi();
  }

  render() {
    return(
      <div>
        { !this.state.isAuthorized ? (
          <button onClick={ this.handleAuthClick } id="authorize-button">Login/Signup</button>
        ) : (
          <div>
            <p>{ "Hello, " + this.state.name + "!" }</p>
            <p>{ "Hello, " + this.state.email + "!" }</p>
            <Main email={ this.state.email }/>
          </div>
        ) }
      </div>
    );
  }
}

Main.js component

class Main extends React.Component {
  constructor(props) {
    super(props);
    this.state = {};
    console.log(this.props);  // PRINTS ==> {email: ""}
  }

  render() {
    return (
      <div> Test </div>
    );
  }
}

export default Main;

In the first component initialization, it will take its props . But later on if you set its props like this.props = newProps , this newProps will be available in componentWillReceiveProps . You can grab newProps here and do whatever you want like assiging to state .

In addition, you can refer here to have a look at React life cycle.

setState() does not always immediately update the component. It may batch or defer the update until later. This makes reading this.state right after calling setState() a potential pitfall. Instead, use componentDidUpdate or a setState callback (setState(updater, callback)), either of which are guaranteed to fire after the update has been applied. If you need to set the state based on the previous state, read about the updater argument below.

Font: https://facebook.github.io/react/docs/react-component.html

React setState is an asynchronous function, sometimes they batch your state and put it on queue, in this case probably it's happening this:

Try to console.log the props inside componentWillReceiveProps

eg

componentWillReceiveProps(nextProps) {
   console.log(nextProps);
}

Reason is constructor get called only once on initial rendering not after that, and you are logging the props values in Child constructor so it will always print the initial props values if you want to see the updated value in Child put the console inside render it will print the updated value.

You can also use componentWillReceiveProps lifecycle method to check the udpated props values.

componentWillReceiveProps() is invoked before a mounted component receives new props.

Like this:

class Main extends React.Component {

  render() {

    //here
    console.log(this.props);

    return (
      <div> Test </div>
    );
  }
}

The problem in your code is that , componentDidMount is called after the first render Call in HomePage.js . So the props are available in the child and since the constructor in the child is called only once, the props don't get updated. You need to make use of componentWillReceiveProps and componentDidMount in the child component if you want to update the state from props in the child or if you directly want to use it, use it in the render

PS Make sure to perform a undefined check in the child

class Main extends React.Component {
  constructor(props) {
    super(props);
    this.state = {};
  }

  componentWillReceiveProps(nextProps) {
      console.log(nextProps);
  }
  render() {
    console.log(this.props.email);
    return (
      <div> Test </div>
    );
  }
}

export default Main;

You can simply refresh state of your child component (Main.js) through componentWillReceiveProps method as follows.

class Main extends React.Component {
  constructor(props) {
    super(props);
    this.state = { data: this.props.dataParentToChild }; // Initial data set
    console.log(this.props);  // PRINTS ==> {email: ""}
  }

  componentWillReceiveProps(nextProps) {
    this.setState({ data: this.nextProps.dataParentToChild }); // Refresh data  
    console.log(this.nextProps);  // PRINTS ==> {email: "updated value"}
  }
  
  render() {
    return (
      <div> Test </div>
    );
  }
}

export default Main;

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