React - HeaderComponent não muda após o login?
Eu tenho um TodoComponentque foi escrito para mostrar certos links na barra de navegação no HeaderComponent, dependendo do estado atual do usuário - conectado ou desconectado.
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>
)
}
}
Sinto que o HeaderComponentnão renderiza novamente depois que o usuário está logado.
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>
)
}
}
porque o componente LogIn não força? (Devo forçar outros componentes? Não tenho certeza se esta é a maneira correta de fazer as coisas)
Snippet de 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)
}
Como faço a atualização do HeaderComponent (basicamente, chamo o rendermétodo novamente após o login do usuário?!?!).
PS: Acho que pode haver outra maneira de fazer isso, que envolve a execução automática de alguma ação com base em um evento. Só não sei como isso se encaixaria no que fiz até agora. Sou novo no React.
Esse cara enfrentou o mesmo problema (mas explicou mal o IMO): habilitar a autenticação de usuário baseada em link de menu não está funcionando no reactjs
Respostas
Os componentes são renderizados novamente se detectar qualquer mudança no estado ou nas propriedades, isso é o que a comparação superficial faz, então, para renderizar novamente o componente, ele deve depender de um deles, então o que você pode fazer é usar isLoginSuccess e passá-lo de volta para TodoComponent e, em seguida, envie-o como um adereço para HeaderComponent como este:
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>
)
}
}
Em LoginComponent
handleLoginButtonClick = () => {
if (this.state.username === 'zaid' && this.state.password === 'khan') {
...
this.props.onLogin(true);
...
}
else {
this.props.onLogin(false);
...
}
}
Em HeaderComponent, substitua isUserLoggedIn por 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: Usar Redux ou Contexto em vez de passar adereços entre componentes seria mais estruturado e fácil de manusear
Você não deve manter seu status de autenticação em uma classe externa. A detecção de mudança de reação dispara apenas se o estado ou props forem diferentes (comparação superficial). Ele não pode detectar mudanças em uma classe externa. Tal estado pode, por exemplo, ser gerenciado com 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)
Lembre-se de embrulhar toda a sua árvore React com AuthProvider.