I am using sessionStorage to keep the auth information and need to replicate this information to other tabs being opened. I have created a codesandbox to recreate the issue. I didn't include code in this post since it is quite lengthy.
When the Dashboard link is opened in another window, it is not rendered due to the error being popped up.
EDIT: I have included all the necessary codes below.
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, setIsLogged] = useState(false);
const [email, setEmail] = useState();
const [token, 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.user.email);
sessionStorage.setItem("token", 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, { useState, useEffect } from "react";
export default (props) => {
const [isLogged, 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: "[email protected]",
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: "[email protected]" },
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>
);
};
Please do let me know what can be the reason for this.
You are getting an error because you are sending undefined data here:
Home.js
Also I recommend you to change this line:
Because every time the component is rendered, a new instance of
BroadcastChannel
is created. To avoid this you need to store theBroadcastChannel
instance inuseRef
: