Question:
How can I re-render the NavMenu component from another Login component? As far as I understand, it is impossible to change the state of some specific other component from one component? Is there a way?
export class Login extends Component {
constructor(props) {
....
}
...
async login() {
var data = {
login: this.state.login,
password: this.state.password
}
await fetch('sendrequest/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
})
.catch(error => console.log(error));
}
...
render() {
return (
<Col lg={3}>
<form>
<input type="email" id="emailInput" onChange={this.onChangeLogin} name="passwordInput" placeholder="E-Mail" />
<input type="password" id="passwordInput" onChange={this.onChangePassword} name="passwordInput" placeholder="Password" />
<Button className="btn btn-primary" onClick={this.login} >Log in</Button>
</form>
<Button className="btn btn-primary" onClick={this.refreshToken}>Refresh Token</Button>
</Col>
)
}
}
The navbar itself looks like this:
...
export class NavMenu extends Component {
static displayName = NavMenu.name;
async componentDidMount() {
await this.getProfileData();
}
constructor(props) {
....
this.state = {
collapsed: true,
firstName: ""
};
}
...
async getProfileData() {
await fetch('sendrequest/getProfileData', {
method: "POST",
headers: {
'Content-Type': "application/json"
}
})
.then(response => response.json())
.then(result => this.setState({
firstName: result.firstName
}));
}
async logout(e) {
e.preventDefault();
await fetch('sendrequest/logout', {
method: "POST",
headers: {
'Content-Type': "application/json"
}
});
this.setState({ firstName: undefined });
}
render() {
var loginMenu = <NavLink tag={Link} className="text-dark" to="/login">Log in</NavLink>;
if (this.state.firstName != null && this.state.firstName != undefined) {
loginMenu = <React.Fragment><NavItem><NavLink tag={Link} className="text-dark" to="/profile">{this.state.firstName}</NavLink></NavItem>
<NavItem><NavLink tag={Link} className="text-dark" to="/profle" onClick={this.logout}>Logout</NavLink></NavItem></React.Fragment>;
}
return (
<header>
<Navbar className="navbar-expand-sm navbar-toggleable-sm ng-white border-bottom box-shadow mb-3" light>
<Container>
<NavbarBrand tag={Link} to="/">LibraryClient</NavbarBrand>
<NavbarToggler onClick={this.toggleNavbar} className="mr-2" />
<Collapse className="d-sm-inline-flex flex-sm-row-reverse" isOpen={!this.state.collapsed} navbar>
<ul className="navbar-nav flex-grow">
<NavItem>
<NavLink tag={Link} className="text-dark" to="/">Home</NavLink>
</NavItem>
<NavItem>
<NavLink tag={Link} className="text-dark" to="/counter">Counter</NavLink>
</NavItem>
<NavItem>
<NavLink tag={Link} className="text-dark" to="/fetch-data">Fetch data</NavLink>
</NavItem>
{loginMenu}
</ul>
</Collapse>
</Container>
</Navbar>
</header>
);
}
}
Answer:
A small note:
The method I suggested may not be the "react way", but it is simple and works.
Perhaps it would be more correct to create an external / global state store.
How can you re-render a component from another component?
Exclusively for example:
import * as React from "react";
const COMPONENT: symbol = Symbol();
export default class App extends React.Component {
tic = 0;
i = setInterval(() => {
this.tic++;
}, 1e3);
ref = (el: HTMLElement | null) => {
// @ts-ignore
if (el) el[COMPONENT] = this;
};
render() {
return (
<div className="App" ref={this.ref}>
<span>{this.tic}</span>
</div>
);
}
}
setInterval(() => {
const app = document.querySelector(".App");
if (app && COMPONENT in app) {
// @ts-ignore
const component: React.Component = app[COMPONENT];
component.forceUpdate();
}
}, 3e3);