React - HeaderComponent non cambia dopo il log-in?
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
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
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.