反应 BroadcastChannel 出错 API

React Getting error with BroadcastChannel API

我正在使用 sessionStorage 来保存授权信息,并且需要将此信息复制到其他打开的选项卡。我创建了一个 codesandbox 来重现问题。我没有在这个 post 中包含代码,因为它太长了。

当仪表板link在另一个window中打开时,由于弹出错误而无法呈现。

编辑: 我在下面包含了所有必要的代码。

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: "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;