2
componentDidMount() {
    const user = auth.getCurrentUser();
    this.setState({ user });
  }

I have this code, I think this.setState({ user });takes a little time, if I want to do some checks immidetly like

<Route
              path="/foo"
              render={props =>
                this.state.user ? (
                  <Bar {...props} />
                ) : (
                  <Redirect to="/login" />
                )
              }
            />

when refreshing the page the user at the beginning is always null. what is the proper solution? do I need to set state at constructor? or did I do something wrong?

My account got blocked by some down votes questions, the funny thing is I have to re-edit them, even though I already have the accepted answer.I do not understand what's the point to do this.I am so frustrated by this stackoverflow system.

Now, I basically can do nothing but keep editing my questions, and they have all been answered. This is ridiculous !!!

Andy Song
  • 4,404
  • 1
  • 11
  • 29
  • _"do I need to set state at constructor?"_, did you try it and how about the result? – You Nguyen Oct 18 '18 at 10:32
  • Use callback or use async/await. Also change this.setState({ user }) to this.setState({ user: user }); – parth Oct 18 '18 at 10:33
  • @NguyễnThanhTú yes I did it's working. if I set state in the `constructor`, just wondering what's the best practices – Andy Song Oct 18 '18 at 10:39
  • @parth there is nothing to do with async and await. there is no promise in that function. and this `this.setState({ user });` is valid js code – Andy Song Oct 18 '18 at 10:41
  • @AndySong I think I know what you're trying to do. Well then, this article might help:https://tylermcginnis.com/react-router-protected-routes-authentication/ – You Nguyen Oct 18 '18 at 10:42

5 Answers5

2

Yes, you should initialise your state in the constructor.

See the React docs example:

class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }

  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}
scrowler
  • 24,273
  • 9
  • 60
  • 92
  • hi, I know, it's working. so what I did in `componentDidMount` is wrong right? when I make api calls we usually do it in `componentDidMount` and when we do some checks immidetly , I should do it in `constructor` ? – Andy Song Oct 18 '18 at 10:45
1

when refreshing the page the user at the beginning is always null

@Shubham Khatri did explain it really well, in short, just because the render() function is called before the componentDidMount(), hence, the user is always null.

Take a look at this: React lifecycle methods diagram

React lifecycle methods diagram

And as you can see that, the proper place for setState should be the contructor() cuz it's called before the render().

However, for api calls why componentDidMount is the better place? why we do not do all set up in constructor?

I know you're talking about this: https://reactjs.org/docs/faq-ajax.html . The document does say that: You should populate data with AJAX calls in the componentDidMount lifecycle method. This is so you can use setState to update your component when the data is retrieved.

However, in another place, they say:

You may call setState() immediately in componentDidMount(). It will trigger an extra rendering, but it will happen before the browser updates the screen. This guarantees that even though the render() will be called twice in this case, the user won’t see the intermediate state. Use this pattern with caution because it often causes performance issues. In most cases, you should be able to assign the initial state in the constructor() instead. It can, however, be necessary for cases like modals and tooltips when you need to measure a DOM node before rendering something that depends on its size or position.

...and also for the case that you need to authenticate because this process depends on the value of the user ( as your design).

You Nguyen
  • 9,961
  • 4
  • 26
  • 52
0

The problem in your code is that, componentDidMount is called after render and by the time your user details are fetched and stored in state, your component is ready to redirect to /login since user wan't available. To solve this issue, you need to fetch user details before the initial render and hence constructor is the ideal place to do it

constructor(props) {
   super(props);
   this.state = {
       user: auth.getCurrentUser()
   }
}
Shubham Khatri
  • 270,417
  • 55
  • 406
  • 400
  • however, for api calls why `componentDidMount` is the better place? why we do not do all set up in `constructor`? – Andy Song Oct 18 '18 at 10:53
  • @AndySong Please check this answer https://stackoverflow.com/questions/47392910/use-componentwillmount-or-componentdidmount-lifecycle-functions-for-async-reques/47393005#47393005 – Shubham Khatri Oct 18 '18 at 10:56
0

The state goes inside the constructor, but only if you need a constructor (e.g.:to initialize flags). If you don't need a constructor you can just initialize the state outside:

class MyComponent extends Component {
    state = { myState = [] }

    render() {
        const { myState } = this.state
        return (...)
    }
}
diegoalmesp
  • 162
  • 1
  • 13
0

You should use the constructor() to initiate state, componentDidMount() for call functions and componentWillReceiveProps() for setState.

  constructor(props) {
    super(props);
    this.state = {
      firstName: "",
      lastName: ""
    };
  }

  componentDidMount() {
    this.props.userProfile();
  }

  componentWillReceiveProps(nextProps, prevProps) {
    this.setState({
      firstName: nextProps.user.firstName,
      lastName: nextProps.user.lastName
    });
  }
Ankit Kumar Rajpoot
  • 5,188
  • 2
  • 38
  • 32