我在App
组件中有一个导航条(组件navbarUser
),我想当用户登录时,更改导航条以显示注销和配置文件。
当用户登录导航条不改变,我在App
组件和NavbarUser
中有一个useEffect
,但是,如果我手动刷新(F5),那么导航条改变。
我的代码问题在哪里?动态改变导航条的解决方案是什么?
我包含了一个视频:video
组件的应用
import React, { useState, useEffect } from "react";
import logo from "./assets/logoBusca.png";
import "./App.css";
import { Route, Switch } from "wouter";
import logic from "./logic";
import Home from "./components/Home";
import Login from "./components/Login";
import Post from "./components/Post";
import NavbarUser from "./components/NavbarUser";
function App() {
const [userLogged, setUserLogged] = useState(false);
useEffect(() => {
(async () => {
const loggedIn = await logic.isUserLoggedIn;
if (loggedIn) setUserLogged(true);
})();
}, [userLogged]);
return (
<div className="App">
<header className="App-header">
<div className="images">
<div className="logo">
<a href="/">
<img src={logo} alt="logo" />
</a>
</div>
<div className="user_flags">
<NavbarUser />
</div>
</div>
</header>
<Switch>
<Route path="/" component={Home} />
<Route path="/login">{() => <Login />}</Route>
<Route path="/nuevabusqueda">{() => <Post />}</Route>
</Switch>
</div>
);
}
export default App;
组件NAVBARUSER
import React, { useState, useEffect } from "react";
import userIcon from "../../assets/userIcon.png";
import logic from "../../logic";
export default function NavbarUser() {
const [navbarUserIsLogged, setnavbarUserIsLogged] = useState(false);
useEffect(() => {
(async () => {
const loggedIn = await logic.isUserLoggedIn;
if (loggedIn) setnavbarUserIsLogged(true);
})();
}, [navbarUserIsLogged]);
return (
<>
{!navbarUserIsLogged ? (
<div className="navbar-item has-dropdown is-hoverable">
<img src={userIcon} alt="user" />
<div className="navbar-dropdown">
<a href="/login" className="navbar-item" id="item_login">
Login
</a>
<hr className="navbar-divider" />
<a href="/registro" className="navbar-item" id="item_register">
Registro
</a>
</div>
</div>
) : (
<div className="navbar-item has-dropdown is-hoverable">
<img src={userIcon} alt="user" />
<div className="navbar-dropdown">
<a href="/datos" className="navbar-item" id="item_login">
Perfil
</a>
<hr className="navbar-divider" />
<a href="/user" className="navbar-item" id="item_register">
Logout
</a>
</div>
</div>
)}
</>
);
}
登录组件
import React, { useState } from "react";
import { useLocation } from "wouter";
import logic from "../../logic";
import "./index.css";
export default function Login() {
const [messageError, setMessageError] = useState("");
const [, pushLocation] = useLocation();
async function handleOnSubmit(e) {
e.preventDefault();
const {
email: { value: email },
password: { value: password }
} = e.target;
e.target.reset();
try {
await logic.loginUser(email, password);
pushLocation("/nuevabusqueda");
} catch (error) {
setMessageError(error.message);
}
}
return (
<>
{messageError && (
<div className="message-error">
<p>{messageError}</p>
</div>
)}
<section className="hero is-fullwidth">
<div className="hero-body">
<div className="container">
<div className="columns is-centered">
<div className="column is-4">
<form id="form" onSubmit={e => handleOnSubmit(e)}>
<div className="field">
<p className="control has-icons-left">
<input
className="input"
name="email"
type="email"
placeholder="Email"
required
/>
<span className="icon is-small is-left">
<i className="fas fa-envelope"></i>
</span>
</p>
</div>
<div className="field">
<p className="control has-icons-left">
<input
className="input"
name="password"
type="password"
placeholder="Password"
required
/>
<span className="icon is-small is-left">
<i className="fas fa-lock"></i>
</span>
</p>
</div>
<div className="field">
<p className="control">
<button className="button is-success">Login</button>
</p>
</div>
<div>
<a href="/registro">
¿Aún no estás registrado? Acceso para registarte
</a>
</div>
</form>
</div>
</div>
</div>
</div>
</section>
</>
);
}
逻辑import buscasosApi from "../data";
const logic = {
set userToken(token) {
sessionStorage.userToken = token;
},
get userToken() {
if (sessionStorage.userToken === null) return null;
if (sessionStorage.userToken === undefined) return undefined;
return sessionStorage.userToken;
},
get isUserLoggedIn() {
return this.userToken;
},
loginUser(email, password) {
return (async () => {
try {
const { token } = await buscasosApi.authenticateUser(email, password);
this.userToken = token;
} catch (error) {
throw new Error(error.message);
}
})();
}
};
export default logic;
具有全局身份验证上下文,以便所有组件都可以成功地意识到在登录/注销之前发生的更改。
当前发生的情况是,即使用户成功登录,NavbarUser
组件也不知道发生了什么,因为没有全局状态在你的应用程序中,基于你的NavbarUser
可以决定改变它自己的状态。
下面的语句只是一个一次性操作,因为navbarUserIsLogged
是NavbarUser
组件的局部,只会改变组件的第一次渲染后,所以这个useEffect
基本上不会一次又一次地运行,仅仅基于navbarUserIsLogged
,因为它永远不会改变。您需要的更改是全局状态更改.
useEffect(() => {
(async () => {
const loggedIn = await logic.isUserLoggedIn;
if (loggedIn) setnavbarUserIsLogged(true);
})();
}, [navbarUserIsLogged]);
<<p>上下文/strong>API为我们解决了这个问题。假设有一个上下文或全局状态对象其中一个键是isAuthenticated
您的NavbarUser
现在可以订阅全局状态中发生的更改当它改变时,你的NavbarUser
组件可以基于它重新渲染:-
/* Suppose AuthContext is our global Context for giving us the global
state or context object. We are de-structuring the isAuthenticated key
from that object here. Suppose the value of isAuthenticated changes in
global state so that will get informed to our component which uses useContext */
const {isAuthenticated} = useContext(AuthContext)
现在基于isAuthenticated
,您可以直接为已验证的用户或未验证的用户呈现导航栏。
目前,即使您在localStorage
中保存相关令牌,您的应用程序中的组件也不知道这些变化正在发生。在页面刷新时,再次读取localStorage
。
通过传递setUserLogged
状态更新程序,甚至可以使您当前的实现工作。函数作为道具传递给每一个子组件并触发它们的更新这样App
和它的每一个子组件都会被重新渲染但是这不是一个好方法去做事情。使用上下文API这里。
下面的答案讨论了上下文API实现- React上下文在第一次登录时返回undefined,但在页面刷新时工作
如往常一样,请参考React文档获取更多参考。分享useContext
在这里- https://reactjs.org/docs/hooks-reference.html#usecontext
创建两个组件
<NavLoggedIn>
//Logged in links and styling goes here
<NavLoggedIn>]
<NavPublic>
//Logged out links and styling goes here
<NavPublic/>
最后,在导航栏组件中,这样做:
const Navbar = () => {
return (
<>
{!isAuth() ? <NavLoggedIn /> : <NavPublic />}
</>
);
};