React - HeaderComponent non cambia dopo il log-in?

Sep 01 2020

Ho un TodoComponentche è stato scritto per mostrare determinati collegamenti sulla barra di navigazione in HeaderComponent a seconda dello stato corrente dell'utente, che sia connesso o disconnesso.

class TodoApp extends Component {
    render = () => {
        return (
            <div className="TodoApp">
                <Router>
                        <HeaderComponent />
                        <Switch>
                            <Route path="/" exact component={LoginComponent} />
                            <Route path="/login" exact component={LoginComponent} />
                            <AuthenticatedRoute path="/welcome/:name" exact component={WelcomeComponent} />
                            <AuthenticatedRoute path="/todos/" exact component={ListTodosComponent} />
                            <AuthenticatedRoute path="/logout" exact component={LogoutComponent} />
                            <Route component={ErrorFuncComponent} />
                        </Switch>
                        <FooterComponent />
                </Router>
            </div>
        )
    }
}

Ritengo che il HeaderComponentrendering non venga più eseguito dopo che l'utente ha effettuato l'accesso.

class HeaderComponent extends Component {

    render() {
        
        const isUserLoggedIn = AuthenticationService.isUserLoggedIn()
        console.log(isUserLoggedIn)

        return (
            <header>
                <nav className="navbar navbar-expand-md navbar-dark bg-dark">
                    <div><a href="http://localhost:3000" className="navbar-brand">Todo Management</a></div>
                    <ul className="navbar-nav">
                        {isUserLoggedIn && <li><Link className="nav-link" to="/welcome/in28minutes">Home</Link></li>}
                        {isUserLoggedIn && <li><Link className="nav-link" to="/todos">Todos</Link></li>}
                    </ul>
                    <ul className="navbar-nav navbar-collapse justify-content-end">
                        {!isUserLoggedIn && <li><Link className="nav-link" to="/login">Login</Link></li>}
                        {isUserLoggedIn && <li><Link className="nav-link" onClick={AuthenticationService.logoutAuthenticatedUser} to="/logout">Logout</Link></li>}
                    </ul>
                </nav>
            </header>
        )
    }
}

perché il componente LogIn non forza? (Devo forzare altri componenti? Non sono sicuro che questo sia il modo corretto di fare le cose)

Snippet di LoginComponent.jsx

handleLoginButtonClick = () => {

    if (this.state.username === 'zaid' && this.state.password === 'khan') {
        AuthenticationService.registerAuthenticatedUser(this.state.username, this.state.password)
        // window.location.reload(false);
        this.props.history.push(`/welcome/${this.state.username}`) //String templating //this.props.history.push("/welcome/"+this.state.username)
        this.setState(
            { isLoginSuccess: true, isLoginFailed: false }, function () {
                console.log(this.state);
            }
        )
    }
    else {
        this.setState(
            { isLoginSuccess: false, isLoginFailed: true }, function () {
                console.log(this.state);
            }
        )
    }
    // console.log(this.state)
}

Come faccio ad aggiornare HeaderComponent (in pratica richiamo di rendernuovo il metodo dopo che l'utente ha effettuato l'accesso?!?!).

PS Penso che potrebbe esserci un altro modo per farlo che implica l'esecuzione automatica di un'azione basata su un evento. Non so come si adatterebbe a quello che ho fatto finora. Sono nuovo in React.

Questo ragazzo ha affrontato lo stesso problema (ma lo ha spiegato male IMO): abilitare l'autenticazione utente basata sul collegamento del menu non funziona in reactjs

Risposte

1 AliAbuHijleh Sep 01 2020 at 08:01

I componenti rieseguono il rendering se rileva cambiamenti in State o Props, questo è ciò che fa il confronto superficiale, quindi per rieseguire il rendering del componente, dovrebbe dipendere da uno se questi, quindi quello che puoi fare è usare isLoginSuccess e passarlo indietro a TodoComponent e quindi inviarlo come oggetti di scena a HeaderComponent in questo modo:

class TodoApp extends Component {
    state = {isLoginSuccess:false}
    render = () => {
        return (
            <div className="TodoApp">
                <Router>
                        <HeaderComponent isLoginSuccess={this.state.isLoginSuccess}/>
                        
                        <Switch>
                            <Route path="/" exact component={() => <LoginComponent isAuthed={true} onLogin={(status)=> this.setState({isLoginSuccess:status})}/>} />
                            ...
                </Router>
            </div>
        )
    }
}

In LoginComponent

handleLoginButtonClick = () => {
    if (this.state.username === 'zaid' && this.state.password === 'khan') {
        ...
        this.props.onLogin(true);
        ...
    }
    else {
        this.props.onLogin(false);
        ...
    }
}

In HeaderComponent sostituire isUserLoggedIn con isLoginSuccess:

class HeaderComponent extends Component {

    render() {
    let { isLoginSuccess } = this.props;
        ...
        return (
            <header>
                <nav className="navbar navbar-expand-md navbar-dark bg-dark">
                    <div><a href="http://localhost:3000" className="navbar-brand">Todo Management</a></div>
                    <ul className="navbar-nav">
                        {isLoginSuccess && <li><Link className="nav-link" to="/welcome/in28minutes">Home</Link></li>}
                        ...
                </nav>
            </header>
        )
    }
}

Nota: l'utilizzo di Redux o Context invece di passare gli oggetti di scena tra i componenti sarebbe più strutturato e facile da gestire

1 Nalhin Sep 01 2020 at 07:43

Non dovresti mantenere il tuo stato di autenticazione in una classe esterna. React change detection si innesca solo se lo stato o gli oggetti di scena sono diversi (confronto superficiale). Non è in grado di rilevare i cambiamenti in una classe esterna. Tale stato può, ad esempio, essere gestito con React Context.

const AuthContext = React.createContext()

function AuthProvider(props) {
  const login = () => {}
  const register = () => {}
  const logout = () => {} 
  const isLoggedIn = ()=>{}
  return (
    <AuthContext.Provider value={{data, login, logout, register, isLoggedIn }} {...props} />
  )
}

export function withAuthContext(Component) {
    return function WrapperComponent(props) {
        return (
            <AuthContext.Consumerr>
                {state => <Component {...props} context={state} />}
            </AuthContext.Consumer>
        );
    };
}

export {AuthProvider, withAuthContext}
class HeaderComponent extends Component {

    render() {
        
        const isUserLoggedIn = props.context.isLoggedIn()

        return (
            <header>
                <nav className="navbar navbar-expand-md navbar-dark bg-dark">
                    <div><a href="http://localhost:3000" className="navbar-brand">Todo Management</a></div>
                    <ul className="navbar-nav">
                        {isUserLoggedIn && <li><Link className="nav-link" to="/welcome/in28minutes">Home</Link></li>}
                        {isUserLoggedIn && <li><Link className="nav-link" to="/todos">Todos</Link></li>}
                    </ul>
                    <ul className="navbar-nav navbar-collapse justify-content-end">
                        {!isUserLoggedIn && <li><Link className="nav-link" to="/login">Login</Link></li>}
                        {isUserLoggedIn && <li><Link className="nav-link" onClick={props.context.logout} to="/logout">Logout</Link></li>}
                    </ul>
                </nav>
            </header>
        )
    }
}

export default withAuthContext(HeaderComponent)

Ricorda di avvolgere l'intero albero React con AuthProvider.