Reputation:
I have the following code: In App.js:
class App extends React.Component {
constructor() {
super();
this.state = {
loggedStatus: {
username: undefined,
isLoggedIn: undefined,
}
}
}
componentDidMount() {
let username = undefined;
let isLoggedIn = undefined;
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) {
username = response.username;
isLoggedIn = true;
} else {
username = undefined;
isLoggedIn = false;
localStorage.removeItem("token");
}
this.setState({
loggedStatus: {
username: username,
isLoggedIn: isLoggedIn
}
})
})
}
}
render() {
return (
<div className="App">
<Router>
<Navbar loggedStatus={this.state.loggedStatus}/>
<Switch>
<Route path="/register">
<RegisterForm />
</Route>
<Route path="/login">
<LoginForm />
</Route>
</Switch>
</Router>
</div>
)
}
}
and in Navbar.js:
logout = () => {
localStorage.removeItem("token");
}
render() {
return (
<nav className="Navbar">
{this.props.loggedStatus.isLoggedIn ?
<>
<ul className="Navbar-list">
<li className="Navbar-item">
<span className="Navbar-greeting">Hello, {this.props.loggedStatus.username}</span>
</li>
<li className="Navbar-item">
<button className="Navbar-logoutBtn" onClick={this.logout}>Sign Out</button>
</li>
</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>
</>
}
</nav>
)
}
The problem I'm having is I would like my navbar component to update when either the user logs in, or logs out. With my current code, I have to refresh the page in order for it to update. I've been messing around with things with no luck. I understand that componentDidMount is only called once through the entire process, which is why setState is only called upon refresh.
Edit: Login.
fetch(`${this.apiURL}/user/login`, {
method: "POST",
body: JSON.stringify(user),
headers: {
"Accept": "application/json",
"Content-Type": "application/json"
}
})
.then(response => response.json())
.then(response => {
if (response.success) {
status = {
statusMsg: <p className="LoginForm-statusMsg">{response.statusMsg}</p>
}
localStorage.setItem("token", response.token);
this.setState({
status: status
}, () => setTimeout(() => {
this.props.history.push("/");
}, 5000));
} else {
status = {
statusMsg: <p className="LoginForm-statusMsg">{response.statusMsg}</p>
}
this.setState({
status : status
})
}
});
}
}
Upvotes: 2
Views: 8418
Reputation: 762
setState({}) always forces to re-render. (unless you return false in: shouldComponentUpdate(nextProps, nextState)) You can check this by putting a console log in
componentDidUpdate(prevProps, prevState) {
console.log("Component did update")
}
It's not clear what your JobsScreenTabs component consists of but make sure that for changes you expect to happen inside the JobsScreenTabs component it actually changes its state. Pass properties from your WorkshopJobsScreen component or make changes directly in the JobsScreenTabs component.
Also important:
Using State Correctly There are three things you should know about setState().
Do Not Modify State Directly For example, this will not re-render a component:
// Wrong
this.state.comment = 'Hello';
Instead, use setState():
// Correct
this.setState({comment: 'Hello'});
React may batch multiple setState() calls into a single update for performance.
Because this.props and this.state may be updated asynchronously, you should not rely on their values for calculating the next state.
Neither parent nor child components can know if a certain component is stateful or stateless, and they shouldn’t care whether it is defined as a function or a class.
This is why state is often called local or encapsulated. It is not accessible to any component other than the one that owns and sets it.
So if you wish to make changes in your component make sure to manipulate the state of the correct component.
Read more about React lifecycles at: https://reactjs.org/docs/state-and-lifecycle.html
So just some general info. Answer starts here:
You can pass your state in props from app js to navbar component through the route:
let loggedStatus = {
username: undefined,
isLoggedIn: false
}
<Route path="/" render={(props) => <NavBar props={loggedStatus} {...props} /> } exact />
In NavBar you can access it:
export class NavBar extends Component {
constructor(props) {
super(props);
console.log(props)
this.state = {
isLoggedIn: this.props.loggedStatus.isLoggedIn
Upvotes: 1
Reputation: 2743
As I'm not aware of the login flow you are following. But as far as what I understood. The code below should match your requirement.
In NavBar.js
logout = () => {
this.props.handleLogout()
}
In app.js
handleLogout = () => {
localStorage.removeItem("token");
this.setState({
loggedStatus: {
username: undefined,
isLoggedIn: false
}
})
}
render(){
return(
..
..
<Navbar loggedStatus={this.state.loggedStatus} handleLogout={this.handleLogout}/>
)
}
The state changes so it will re-render the component.
Hope this helps you.
Upvotes: 0