问题描述
我使用 sessionStorage 来保存身份验证信息,并且需要将此信息复制到正在打开的其他选项卡。我创建了一个 codesandbox 来重现该问题。我没有在这篇文章中包含代码,因为它很长。
Dashboard 链接在另一个窗口中打开时,由于弹出错误而未呈现。
编辑: 我在下面包含了所有必要的代码。
App.js
export default function App() {
return (
<>
<Router>
<Switch>
<Route exact path="/">
<Home />
</Route>
<Route path="/login">
<Login />
</Route>
<Route path="/dashboard">
<Dashboard />
</Route>
</Switch>
</Router>
</>
);
}
Dashboard.js
export default (props) => {
const [isLogged,setIsLogged] = useState(false);
const [email,setEmail] = useState();
const [token,setToken] = useState();
const authChannel = new broadcastChannel("auth");
useEffect(() => {
setToken(sessionStorage.getItem("token"));
setEmail(sessionStorage.getItem("email"));
setIsLogged(sessionStorage.getItem("token") ? true : false);
authChannel.postMessage({ action: "created",source: "Dashboard" });
},[]);
useEffect(() => {
authChannel.onmessage = function (e) {
if (
e.data.action === "login" &&
(e.data.target === "*" || e.data.target === "Dashboard")
) {
setIsLogged(true);
setEmail(e.data.data.user.email);
setToken(e.data.data.token);
sessionStorage.setItem("email",e.data.data.user.email);
sessionStorage.setItem("token",e.data.data.token);
}
};
},[authChannel]);
return (
<>
<h1>Dashboard</h1>
{isLogged && <h1>You are logged in</h1>}
{!isLogged && <h1>Please log in</h1>}
</>
);
};
Home.js
import React,{ useState,useEffect } from "react";
export default (props) => {
const [isLogged,setToken] = useState();
const [user,setUser] = useState();
const authChannel = new broadcastChannel("auth");
useEffect(() => {
setToken(sessionStorage.getItem("token"));
setEmail(sessionStorage.getItem("email"));
setIsLogged(sessionStorage.getItem("token") ? true : false);
},[]);
useEffect(() => {
authChannel.onmessage = function (e) {
if (
e.data.action === "login" &&
(e.data.target === "*" || e.data.target === "Home")
) {
setIsLogged(true);
setEmail(e.data.data.user.email);
setToken(e.data.data.token);
setUser(e.data.data.user);
sessionStorage.setItem("email",e.data.data.token);
} else if (e.data.action === "created") {
authChannel.postMessage({
action: "login",target: e.data.source,data: { user: user,token: token }
});
}
};
},[authChannel]);
const logout = () => {
setIsLogged(false);
setToken(null);
setUser({});
setEmail();
sessionStorage.removeItem("email");
sessionStorage.removeItem("token");
};
return (
<p>
{isLogged && (
<>
<p>
<a href="/dashboard">Dashboard</a>
</p>
<button onClick={() => logout()}>logout</button>
</>
)}
{!isLogged && (
<>
<p>
Please <a href="/login">login</a>
</p>
</>
)}
</p>
);
};
Login.js
import React,setIsLogged] = useState(false);
const [token,setToken] = useState();
const [email,setEmail] = useState();
const [user,setUser] = useState({});
const authChannel = new broadcastChannel("auth");
const [input,setInput] = useState({
email: "superadmin@a.com",password: "quantum"
});
useEffect(() => {
authChannel.onmessage = function (e) {
if (e.data.action === "logout") {
setIsLogged(false);
setToken();
setUser({});
}
};
},[authChannel]);
useEffect(() => {
setToken(sessionStorage.getItem("token"));
setEmail(sessionStorage.getItem("email"));
setIsLogged(sessionStorage.getItem("token") ? true : false);
},[]);
const handleLogin = () => {
// handle login here...
authChannel.postMessage({
action: "login",target: "*",data: {
user: { email: "a@a.com" },token: "kjdhlfjkgdjgdlfghjldhjgh76lgj"
}
});
setIsLogged(true);
};
const handleInputChange = (e,source) => {
setInput({ ...input,[source]: e.target.value });
};
return (
<div>
{!isLogged && (
<>
<div>
<label>Email</label>
<input
type="text"
value={input.email || ""}
onChange={(e) => handleInputChange(e,"email")}
/>
</div>
<div className="flex">
<label>Password</label>
<input
type="password"
value={input.password || ""}
onChange={(e) => handleInputChange(e,"password")}
/>
</div>
<button onClick={() => handleLogin()}>Login</button>
</>
)}
{isLogged && <h1>You are logged Now</h1>}
</div>
);
};
请告诉我这可能是什么原因。
解决方法
您收到一个错误,因为您在这里发送未定义的数据:
Home.js
authChannel.postMessage({
action: "login",target: e.data.source,data: { user: user,token: token } //--> user and token are undefined
});
另外我建议你改变这一行:
const authChannel = new BroadcastChannel("auth");
因为每次渲染组件时,都会创建一个新的 BroadcastChannel
实例。为避免这种情况,您需要将 BroadcastChannel
实例存储在 useRef
中:
const authChannelRef = useRef(new BroadcastChannel("auth"));
const authChannel = authChannelRef.current;