由于 16.9 中的 useEffect 钩子导致应用程序冻结,而在 16.8 中工作正常

React app freezing due to useEffect hook in 16.9 while working fine in 16.8

代码在 React 16.8 上运行良好,但在 >=16.9 上冻结。我查看了 16.9 的变化,但它提到的只是一个额外的 警告 当存在无限循环时,而不是实际的行为变化,所以这不应该发生,但它确实发生了。

将 useEffect 的依赖设置为 [] 确实打破了循环,但这意味着代码需要修改,我不确定如何修改。

下面的代码也可以在沙箱中找到 here。版本设置为16.8.4所以不会卡顿。

index.jsx

//...
import { SessionContext, getSessionCookie, setSessionCookie } from "./session";
import "./styles.css";

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 (
    //...
  );
};

const LogoutHandler = ({ history }: any) => {
  //...
};

const ProtectedHandler = ({ history }) => {
   //...
};

const Routes = () => {
  const [session, setSession] = useState(getSessionCookie());
  useEffect(
    () => {
      setSession(getSessionCookie());
    },
    [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.ts

import React from "react";
import * as Cookies from "js-cookie";

export const setSessionCookie = (session: any): void => {
  Cookies.remove("session");
  Cookies.set("session", session, { expires: 14 });
};

export const getSessionCookie: any = () => {
  const sessionCookie = Cookies.get("session");

  if (sessionCookie === undefined) {
    return {};
  } else {
    return JSON.parse(sessionCookie);
  }
};

export const SessionContext = React.createContext(getSessionCookie());

预期结果:应用程序在 16.9 中的功能与在 16.8 中一样

实际结果:由于 useEffect 中假设的无限循环,应用在 16.9 中冻结

错误消息:警告:超出最大更新深度。当组件在 useEffect 中调用 setState,但 useEffect 没有依赖项数组,或者依赖项之一在每次渲染时发生变化时,就会发生这种情况。

JSON.parse(sessionCookie)总是returns一个新对象。

setSession(getSessionCookie()) 将会话设置为该新对象

[session] 告诉 运行 每当在状态中设置新的会话对象(每次渲染都会发生)。

这就是您看到的无限循环。要停止它,您可以执行以下操作:

useEffect(
    () => {
      const newSessionCookie = getSessionCookie()
      if(newSessionCookie.uniqueId !== session.uniqueId) {
        setSession(getSessionCookie());
      }
    },
    [session]
  )

您只需要确保您有一些 cookie 的唯一标识符。