再次编辑以解释===
比较对象(Why are two identical objects not equal to each other?)的内存位置,这就是useEffect()
不断运行的原因……我想我拥有它现在进行了排序,但是如果我说的是绝对的胡言乱语,并且有人想纠正我,将在这里暂时没有答案,这会结束:)
---第二次编辑结束----
我已经编辑了Routes
常量,以将其他sessions
记录到控制台。如您所见,它们是相等的,但由于某种原因,计算机似乎并不这么认为。有谁知道为什么吗?
edited Routes (to show the values)
const Routes = () => {
const [session, setSession] = useState(getSessionCookie());
console.log('Route session before: ', session);
useEffect(
() => {
//setSession(getSessionCookie());
const stateCookie = session;
const getCookie = getSessionCookie();
console.log('stateCookie: ', stateCookie);
console.log('getCookie: ', getCookie);
console.log('Are equal? ', stateCookie === getCookie);
},
[session]
);
console.log('Route session after: ', session);
-----编辑结束-------
我在网上找到了一个tutorial来协助我管理用户会话,但是除非重新使用useEffect依赖项,否则我在重新呈现组件方面一直遇到问题。我已经记录了session
变量的值和类型,但是它没有变化,所以我不明白为什么重新渲染一直在发生。任何帮助将不胜感激。
index.js
import React, { useEffect, useState, useContext } from 'react';
import { render } from "react-dom";
import { Router, Switch, Route } from "react-router";
import { Link } from "react-router-dom";
import { createBrowserHistory } from "history";
import Cookies from "js-cookie";
import { SessionContext, getSessionCookie, setSessionCookie } from "./session";
const history = createBrowserHistory();
const LoginHandler = ({ history }) => {
const [email, setEmail] = useState("");
const [loading, setLoading] = useState(false);
const handleSubmit = async e => {
e.preventDefault();
setLoading(true);
// NOTE request to api login here instead of this fake promise
await new Promise(r => setTimeout(r(), 1000));
setSessionCookie({ email });
history.push("/");
setLoading(false);
};
if (loading) {
return <h4>Logging in...</h4>;
}
return (
<div style={{ marginTop: "1rem" }}>
<form onSubmit={handleSubmit}>
<input
type="email"
placeholder="Enter email address"
value={email}
onChange={e => setEmail(e.target.value)}
/>
<input type="submit" value="Login" />
</form>
</div>
);
};
const ProtectedHandler = ({ history }) => {
const session = useContext(SessionContext);
if (session.email === undefined) {
history.push("/login");
}
return (
<div>
<h6>Protected data for {session.email}</h6>
<Link to="/logout">Logout here</Link>
</div>
);
};
const LogoutHandler = ({ history }) => {
useEffect(
() => {
Cookies.remove("session");
history.push("/login");
},
[history]
);
return <div>Logging out!</div>;
};
const Routes = () => {
const [session, setSession] = useState(getSessionCookie());
console.log('Routes session before: ', session);
console.log('Routes session before typeof: ', typeof session);
useEffect(
() => {
setSession(getSessionCookie());
},
[session] // <-------------- this is the dependency that seems to be causing the trouble
);
console.log('Routes session: ', session);
console.log('Routes session typeof: ', typeof session);
return (
<SessionContext.Provider value={session}>
<Router history={history}>
<div className="navbar">
<h6 style={{ display: "inline" }}>Nav Bar</h6>
<h6 style={{ display: "inline", marginLeft: "5rem" }}>
{session.email || "No user is logged in"}
</h6>
</div>
<Switch>
<Route path="/login" component={LoginHandler} />
<Route path="/logout" component={LogoutHandler} />
<Route path="*" component={ProtectedHandler} />
</Switch>
</Router>
</SessionContext.Provider>
);
};
const App = () => (
<div className="App">
<Routes />
</div>
);
const rootElement = document.getElementById("root");
render(<App />, rootElement);
session.js
import React from "react";
import * as Cookies from "js-cookie";
export const setSessionCookie = (session) => {
Cookies.remove("session");
Cookies.set("session", session, { expires: 14 });
};
export const getSessionCookie = () => {
const sessionCookie = Cookies.get("session");
if (sessionCookie === undefined) {
console.log('undefined');
return {};
} else {
return return JSON.parse(sessionCookie);
}
};
export const SessionContext = React.createContext(getSessionCookie());
答案 0 :(得分:1)
在使用useEffect
时这是一个非常常见的问题,您不应该在依赖项数组中有一个对象,因为Object仅引用其引用,而不是实际值。创建会话时,即使您的首选属性值相同,它也会有一个新引用=>这就是为什么它会创建无限循环的原因。
如果将session
用作依赖项,则应该显式比较属性值,例如session.value
。
我没有使用太多上下文API,但是我想您的<Routes />
组件中可能有问题,<Routes />
中的会话可能不需要更新,因为它只是作为提供者而已角色。通常,这是您分配初始上下文值的地方。
用户成功登录后,您可以在<LoginHandler />
内更新会话。消耗上下文值的其他子组件仅需要使用useContext
来获取最新的会话值。
因此,基本上您的应用程序可能看起来像这样:
// sessionContext.js
const SessionContext = React.createContext({
session: {},
setSession: () => {},
});
// components/routes.js
const Routes = () => {
const [session, setSession] = useState({})
const contextSession = {
session,
setSession
}
return (
<SessionContext.Provider value={contextSession}>
{children}
</SessionContext.Provider>
)
}
// components/childComponent.js
const ChildComponent = () => {
const { session } = useContext(SessionContext)
if (!session)
return null;
return <div>Logged-in</div>
}
对于复杂的状态管理,我建议您看一下redux,您不需要像上面的示例那样使用上下文。
答案 1 :(得分:0)
鉴于您仅应在更新/更改会话时才记录会话,因此应在更新状态之前进行比较,否则,由于您不断更新状态,将导致无限循环。
useEffect(() => {
// assuming that session is not an array or object
if (getSessionCookie().email === session.email) {
return;
}
setSession(getSessionCookie());
}, [session]);
同样,在LogoutHandler
组件上,您不应将history
对象作为依赖项数组的一部分进行更新。实际上,不需要调用history.push()
,因为在呈现组件时您应该已经在该路由中。您只应该删除一次cookie,这样就可以在安装组件时调用它。
const LogoutHandler = ({ history }) => {
useEffect(() => {
Cookies.remove("session");
}, []);
return <div>Logging out!</div>;
};