React useContext 不会更新子组件,但会在页面刷新时起作用
React useContext doesn't update the child component, but works on page refresh
我有一个带有子菜单的导航栏组件。登录后,导航栏的子菜单应该改变。
我使用了钩子 useContext,但它不会在用户登录时刷新导航栏组件。当我刷新页面时它工作正常。
我的代码问题在哪里?
应用组件
import React, { useState, useEffect } from "react";
import logo from "./assets/logoBusca.png";
import "./App.css";
import { Route } from "wouter";
import Home from "./components/Home";
import Login from "./components/Login";
import Post from "./components/Post";
import NavbarUser from "./components/NavbarUser";
import { AuthContext } from "./context/AuthContext";
import logic from "../src/logic";
function App() {
const [user, setUser] = useState(false);
useEffect(() => {
(async () => {
const loggedIn = await logic.isUserLoggedIn;
if (loggedIn) setUser(true);
})();
}, [user]);
return (
<AuthContext.Provider value={user}>
<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>
<Route path="/">
<Home />
</Route>
<Route path="/login">
<Login />
</Route>
<Route path="/nuevabusqueda">
<Post />
</Route>
</div>
</AuthContext.Provider>
);
}
export default App;
导航栏组件
import React, { useContext } from "react";
import userIcon from "../../assets/userIcon.png";
import { AuthContext } from "../../context/AuthContext";
export default function NavbarUser() {
const isAuthenticated = useContext(AuthContext);
return (
<>
{!isAuthenticated ? (
<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 { createContext } from "react";
export const AuthContext = createContext();
逻辑组件
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;
我认为 Login 组件没有调用 setUser(true)
这是它可能如何工作的示例。
const { useState, useEffect, createContext, useContext } = React;
const AuthContext = createContext();
const Login = () => {
const [isAuthenticated, setAuth] = useContext(AuthContext);
const onClick = () => setAuth(true);
return <button disabled={isAuthenticated} onClick={onClick}>Login</button>
}
const App = () => {
const [isAuthenticated] = useContext(AuthContext);
return <div>
<Login/>
<button disabled={!isAuthenticated}>{isAuthenticated ? "Authenticated" : "Not Authenticated"}</button>
</div>;
}
const AuthProvider = ({children}) => {
const [isAuthenticated, setAuth] = useState(false);
return <AuthContext.Provider value={[isAuthenticated, setAuth]}>
{children}
</AuthContext.Provider>;
}
ReactDOM.render(
<AuthProvider>
<App />
</AuthProvider>,
document.getElementById('root')
);
<script src="https://unpkg.com/react/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<div id="root"></div>
在我看来你没有更新状态。最初你的状态是 false
。第一次渲染后,你的效果被触发并且状态再次设置为 false
。在那之后你的效果不再 运行 了,因为它取决于它应该改变的状态。没有状态变化 - 没有效果 - 没有状态再次变化。另外,如果状态发生变化,它会触发效果,但你不需要这个。
您的目标是构建一个系统:
- 检查当前负载状态
- 更改时更新(或重新检查)状态
为此,您需要一些方法来异步发送来自 logic
的消息以做出反应。您可以通过某种订阅来完成此操作,例如:
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;
this.notify(true);
} catch (error) {
throw new Error(error.message);
}
})();
},
subscribers: new Set(),
subscribe(fn) {
this.subscribers.add(fn);
return () => {
this.subscribers.remove(fn);
};
},
notify(status) {
this.subscribers.forEach((fn) => fn(status));
},
};
function useAuthStatus() {
let [state, setState] = useState("checking");
let setStatus = useCallback(
(status) => setState(status ? "authenticated" : "not_authenticated"),
[setState]
);
useEffect(function () {
return logic.subscribe(setStatus);
}, []);
useEffect(function () {
setStatus(logic.isUserLoggedIn);
}, []);
return state;
}
注意,现在有三种可能的状态 - 'checking'、'authenticated' 和 'not_authenticated'。它更详细,可以防止一些错误。例如,如果您希望在用户未通过身份验证时将用户重定向到登录页面。
我有一个带有子菜单的导航栏组件。登录后,导航栏的子菜单应该改变。 我使用了钩子 useContext,但它不会在用户登录时刷新导航栏组件。当我刷新页面时它工作正常。 我的代码问题在哪里?
应用组件
import React, { useState, useEffect } from "react";
import logo from "./assets/logoBusca.png";
import "./App.css";
import { Route } from "wouter";
import Home from "./components/Home";
import Login from "./components/Login";
import Post from "./components/Post";
import NavbarUser from "./components/NavbarUser";
import { AuthContext } from "./context/AuthContext";
import logic from "../src/logic";
function App() {
const [user, setUser] = useState(false);
useEffect(() => {
(async () => {
const loggedIn = await logic.isUserLoggedIn;
if (loggedIn) setUser(true);
})();
}, [user]);
return (
<AuthContext.Provider value={user}>
<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>
<Route path="/">
<Home />
</Route>
<Route path="/login">
<Login />
</Route>
<Route path="/nuevabusqueda">
<Post />
</Route>
</div>
</AuthContext.Provider>
);
}
export default App;
导航栏组件
import React, { useContext } from "react";
import userIcon from "../../assets/userIcon.png";
import { AuthContext } from "../../context/AuthContext";
export default function NavbarUser() {
const isAuthenticated = useContext(AuthContext);
return (
<>
{!isAuthenticated ? (
<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 { createContext } from "react";
export const AuthContext = createContext();
逻辑组件
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;
我认为 Login 组件没有调用 setUser(true)
这是它可能如何工作的示例。
const { useState, useEffect, createContext, useContext } = React;
const AuthContext = createContext();
const Login = () => {
const [isAuthenticated, setAuth] = useContext(AuthContext);
const onClick = () => setAuth(true);
return <button disabled={isAuthenticated} onClick={onClick}>Login</button>
}
const App = () => {
const [isAuthenticated] = useContext(AuthContext);
return <div>
<Login/>
<button disabled={!isAuthenticated}>{isAuthenticated ? "Authenticated" : "Not Authenticated"}</button>
</div>;
}
const AuthProvider = ({children}) => {
const [isAuthenticated, setAuth] = useState(false);
return <AuthContext.Provider value={[isAuthenticated, setAuth]}>
{children}
</AuthContext.Provider>;
}
ReactDOM.render(
<AuthProvider>
<App />
</AuthProvider>,
document.getElementById('root')
);
<script src="https://unpkg.com/react/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<div id="root"></div>
在我看来你没有更新状态。最初你的状态是 false
。第一次渲染后,你的效果被触发并且状态再次设置为 false
。在那之后你的效果不再 运行 了,因为它取决于它应该改变的状态。没有状态变化 - 没有效果 - 没有状态再次变化。另外,如果状态发生变化,它会触发效果,但你不需要这个。
您的目标是构建一个系统:
- 检查当前负载状态
- 更改时更新(或重新检查)状态
为此,您需要一些方法来异步发送来自 logic
的消息以做出反应。您可以通过某种订阅来完成此操作,例如:
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;
this.notify(true);
} catch (error) {
throw new Error(error.message);
}
})();
},
subscribers: new Set(),
subscribe(fn) {
this.subscribers.add(fn);
return () => {
this.subscribers.remove(fn);
};
},
notify(status) {
this.subscribers.forEach((fn) => fn(status));
},
};
function useAuthStatus() {
let [state, setState] = useState("checking");
let setStatus = useCallback(
(status) => setState(status ? "authenticated" : "not_authenticated"),
[setState]
);
useEffect(function () {
return logic.subscribe(setStatus);
}, []);
useEffect(function () {
setStatus(logic.isUserLoggedIn);
}, []);
return state;
}
注意,现在有三种可能的状态 - 'checking'、'authenticated' 和 'not_authenticated'。它更详细,可以防止一些错误。例如,如果您希望在用户未通过身份验证时将用户重定向到登录页面。