why are changes in localStorage not updated until refresh?

Viewed 1451

I am new to using localStorage and I am trying to use localStorage to store the login state so the logout button will appear if the user is logged in and the login button will appear if the user is logged out.

Why localStorage isn't being changed before the redirect if it is being called before the redirect? When I click the login button in the login component the localStorage should be updated and the logout navbar link in app.js should be shown but it isn't shown until I refresh the page. Why does this happen and how can I make the logout button be shown right when I redirect to a the new page?

login.component.js

    onSubmit(e) {
        e.preventDefault()
        this.userdata = {
            username: this.state.username,
            password: this.state.password,
        };
        services.userlogin(this.userdata).then(res => {
                if (this._isMounted) {
                    console.log("Is mounted")
                    if (res === "Successfully authenticated user") {
                        // store the user in localStorage
                        console.log("changed");
                        localStorage.setItem('loggedIn', "true") -> loggedIn is set to true
                        localStorage.setItem('isloggingIn', "false") -> isLoggingIn is set to false
                        localStorage.setItem('username', this.userdata.username)
                    }
                    this.setState({errormessage: res, username:'', password:''});
                }
                else {
                    console.log("Is unmounted")
                }
        })
    }
    error()
    {
        if (this.state.errormessage === "Successfully authenticated user") {
            // store the user in localStorage
            return (<Redirect from="/login-component" to={{pathname: '/ShopDetail', state: this.state}}/>); -> Redirects to ShopDetail page but logout button isn't shown 

        }
        else{
            return (<span className="span">{this.state.errormessage}</span>);
        }
    }
    render() {
        return (
            <div className="div">
                <Form onSubmit={this.onSubmit} className="formWidth">
                    <Form.Group className="group" controlId="Username">
                        <Form.Label style={{display: 'flex', justifyContent: 'center'}}>Username</Form.Label>
                        <Form.Control type="text" value={this.state.username} onChange={this.onChangeusername}/>
                        {this.state.usernameerrormessage.length > 0 &&
                        <span className="span">{this.state.usernameerrormessage}</span>}
                    </Form.Group>
                    <Form.Group className="group" controlId="Password">
                        <Form.Label style={{display: 'flex', justifyContent: 'center'}}>Password</Form.Label>
                        <Form.Control type="text" value={this.state.password} onChange={this.onChangepassword}/>
                        {this.state.passworderrormessage.length > 0 &&
                        <span className="span">{this.state.passworderrormessage}</span>}
                    </Form.Group>
                    <Button variant="danger" size="lg" block="block" type="submit"> Login </Button>
                </Form>
                {this.error()}

            </div>
        );
    }
};

APP.js

constructor(){
    this.state = {isLoggingIn: localStorage.getItem('isloggingIn'), isLoggedIn: localStorage.getItem('loggedIn') ;
}
componentDidMount() {
        this.setState({isLoggingIn: localStorage.getItem('isloggingIn'), isLoggedIn: localStorage.getItem('loggedIn') });
    }
const {isLoggingIn, isLoggedIn} = this.state;
<Nav className="ml-auto">
          <Nav>
               {isLoggedIn==="false" && isLoggingIn==="false" && <Link to={'/login-component'} onClick={this.login} className="nav-link">Login</Link>}
          </Nav>
          <Nav>
               {isLoggedIn==="false" && isLoggingIn==="false" && <Link to={'/createuser-component'} className="nav-link">Sign up</Link>}
          </Nav>
          <Nav>
               {isLoggedIn==="true" && isLoggingIn==="false" && <Link to={'/'} onClick={this.logout} className="nav-link">Logout</Link>} -> ***isLoggedIn is set to true and isLoggingIn is set to false in async userlogin function but the change is not reflected here unless the page is refreshed***
          </Nav>
   </Nav>

I have been trying to fix this for many days now but I really don't understand why this is happening at all.

2 Answers

You can pass down a function reference from App.js to the sub component. This function takes a new state value and executes setState. In the sub component you can then call this function with the new value

localStorage is not reactive, so after Successfully authenticated user you need to setState

if (res === "Successfully authenticated user") {
    // store the user in localStorage
    console.log("changed");
    localStorage.setItem('loggedIn', "true") -> loggedIn is set to true
    localStorage.setItem('isloggingIn', "false") -> isLoggingIn is set to false
    localStorage.setItem('username', this.userdata.username)

    // calling this.setState here
    this.setState({
      loggedIn: true,
      isLoggingIn: false,
      username: this.userdata.username,
    });
}

But the better choice will be updating localStorage whenever state is changed. I am not expert at React and not sure what is the best way to handle it.

// set state on successful auth
if (res === "Successfully authenticated user") {
    // calling this.setState here
    this.setState({
      loggedIn: true,
      isLoggingIn: false,
      username: this.userdata.username,
    });
}

// update localStorage when state changes
componentDidUpdate(_, prevState) {
    if (prevState.loggedIn !== this.state.loggedIn) {
        localStorage.setItem('loggedIn', this.state.loggedIn)
    }
    ...
}
Related