user12346820
user12346820

Reputation:

Child component does not get state from parent

I have the following code, in App.js;

constructor() {
    super();
    this.state = {
        isLoggedIn: false,
        username: "",
    }
}

componentDidMount() {
    let isLoggedIn = false;
    let username = "";

    if (localStorage.getItem("token")) {
        fetch("https://localhost:8000/user", {
            method: "POST",
            headers: {
                "Authorization": `Bearer ${localStorage.getItem("token")}`
            }
        }).then(response => response.json()).then(response => {
            if (response.success) {
                isLoggedIn = true;
                username = response.username;
            } else {
                isLoggedIn = false;
                username = "";
                localStorage.removeItem("token");
            }

            this.setState({
                isLoggedIn : isLoggedIn,
                username: username
            })
        })
    }
}

render() {

    return (
       <Navbar username={this.state.username}/>
    )
}

By console logging the output, once componentDidMount is called the username in state is assigned as it should be. In Navbar.js;

constructor(props) {
    super(props);
    this.state = {
        navComponents: []
    }
}

componentDidMount()  {
    console.log(this.props.username);
    let navComponents = [];
    if (this.props.username === "") { //Not Signed In.
        navComponents.push(
            <ul className="Navbar-list">
                <li className="Navbar-item">
                    <Link to="/register" className="Navbar-link">Sign Up</Link>
                </li>
                <li className="Navbar-item">
                    <Link to="/login" className="Navbar-link">Log In</Link>
                </li>
            </ul>
        );
    } else {
        navComponents.push(
            <p>Hello, {this.props.username}</p>
        )
        navComponents.push(
            <ul className="Navbar-item">
                <Link to="/signout" className="Navbar-link">Sign Out</Link>
            </ul>
        )
    }
    this.setState({
        navComponents : navComponents
    })
}

I believe it should be getting username from App. However, username is always an empty string(The initial state of App). I'm unsure as to why Navbar isn't updating once component is mounted in App.

Upvotes: 0

Views: 32

Answers (2)

Riaz Laskar
Riaz Laskar

Reputation: 1322

Your doing it wrong, isn't ur render function suppose to do the markup for a component, try like this should work fine.

constructor(props) {
    super(props);
}



render(){
    return (
            {this.props.username ? 
             <> 
              <p>Hello, {this.props.username}</p>
              <ul className="Navbar-item">
                <Link to="/signout" className="Navbar-link">Sign Out</Link>
             </ul>
             </>
             : 
            <ul className="Navbar-list">
                <li className="Navbar-item">
                    <Link to="/register" className="Navbar-link">Sign Up</Link>
                </li>
                <li className="Navbar-item">
                    <Link to="/login" className="Navbar-link">Log In</Link>
                </li>
            </ul>})
}

Upvotes: 1

Isaac
Isaac

Reputation: 12874

The reason is very simple, because componentDidMount of Navbar only being fired once, before username has a value.

What's happening with the flow is as below

  1. App.js render being invoked
  2. App.js componentDidMount being invoked
  3. App.js fire API call
  4. Navbar render being invoked
  5. Navbar componentDidMount being invoked
  6. Result of Step 3 coming back

Now because componentDidMount of Navbar will only be fired once, hence you will always see this.props.username is empty there. One of the simplest solution is by to handle your logic at componentDidUpdate instead of componentDidMount

Upvotes: 0

Related Questions