由于 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 的唯一标识符。
代码在 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 的唯一标识符。