javascript – Call navbar state update from another component

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);
Scroll to Top