我遇到了无法解决的问题。在 ContextProvider 组件内的 handleLogIn 函数中,如果用户成功登录,我将使用 useNavigate 挂钩将页面重定向到“/”。但是,用户成功登录后,他们不会重定向到主页;而是会重定向到主页面。相反,他们会被重定向回登录页面。提交第二个表单后,页面重定向正常工作。
如何在表单提交后立即将用户从“/sign-in”或“/sign-up”页面重定向?
这是我的 contextProviderComponent:
export const ContextAPI = createContext<null | TContextAPI>(null);
const ContextProvider = ({ children }: { children: React.ReactNode }) => {
const navigate = useNavigate();
const [currentUser, setCurrentUser] = useState<firebase.User | undefined | null>(undefined);
const [currentUserId, setCurrentUserId] = useState<string | undefined | null>(undefined);
const [loading, setLoading] = useState<boolean>(true);
const { signUpValues, SignUpInputConstructor, handleRegister, signUpError } = useAuth();
const { logInValues, SignInInputConstructor } = useLogIn();
const { handleLogout, logOutError } = useLogOut();
const { newGroupName, isShowGroupCreator, setIsShowGroupCreator, setNewGroupName } = useGroupMenu();
const handleUserGroups = () => {
if (currentUserId) {
const groupRef = doc(db, `/user_groups/${currentUserId}/${newGroupName}`, uuid());
if (newGroupName === "" || newGroupName.length > 20) {
console.log("group name is incorrect");
} else {
setDoc(groupRef, { merge: true });
console.log("db updated sucessfully");
navigate("/");
}
} else {
console.log("currentUserId is not set");
}
};
const [logInError, setLogInError] = useState<string>("");
const logIn = (email: string, password: string) => {
return auth.signInWithEmailAndPassword(email, password);
};
const handleLogIn = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
if (!/^[a-zA-Z\s].*@.*$/.test(logInValues.email)) {
return setLogInError("email is not correct");
}
if (logInValues.password === "") {
return setLogInError("password field can not be empty");
} else {
try {
setLogInError("");
setLoading(true);
logIn(logInValues.email, logInValues.password).then(() => {
navigate("/");
});
} catch (errors) {
setLogInError("Failed to log-in in account");
}
setLoading(false);
}
};
const vals: TContextAPI = {
setNewGroupName,
isShowGroupCreator,
setIsShowGroupCreator,
newGroupName,
currentUserId,
handleUserGroups,
handleLogout,
logOutError,
setLoading,
currentUser,
signUpValues,
SignUpInputConstructor,
handleRegister,
loading,
signUpError,
logInValues,
SignInInputConstructor,
handleLogIn,
logInError,
};
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged((user) => {
setCurrentUser(user);
setLoading(false);
setCurrentUserId(user?.uid || null);
});
return unsubscribe;
}, []);
return <ContextAPI.Provider value={vals}> {!loading && children} </ContextAPI.Provider>;
};
这是我的登录组件:
const LogIn = () => {
const SignInInputConstructor = useContextSelector(ContextAPI, (v) => v?.SignInInputConstructor);
const loading = useContextSelector(ContextAPI, (v) => v?.loading);
const currentUser = useContextSelector(ContextAPI, (v) => v?.currentUser);
const handleLogIn = useContextSelector(ContextAPI, (v) => v?.handleLogIn);
const logInError = useContextSelector(ContextAPI, (v) => v?.logInError);
return (
<>
<h1>Log In page</h1>
<form
onSubmit={(e) => {
handleLogIn?.(e);
}}
>
{currentUser?.email}
{logInError && <Alert severity="error">{logInError}</Alert>}
{SignInInputConstructor?.map((item) => (
<>
<Typography>{item.typography}</Typography>
<TextField
id={item.id}
placeholder={item.placeholder}
variant={item.variant}
value={item.value}
onChange={item.onChange}
/>
</>
))}
<Button variant="contained" type="submit" disabled={loading}>
Log In
</Button>
<Typography>
Need an account? <Link to={"/sign-up"}>Sign Up</Link>
</Typography>
</form>
</>
);
};
这是我的私有路由组件:
const PrivateRoute = () => {
const currentUser = useContextSelector(ContextAPI, (v) => v?.currentUser);
if (currentUser === undefined) return null;
return currentUser ? <Outlet /> : <Navigate to="/sign-in" />;
};
export default PrivateRoute;
最后一个是我所有路线的主要组件:
const Main = () => {
const router = createBrowserRouter(
[
{
path: "/",
element: <App />,
children: [
{
path: "/sign-in",
element: <LogIn />,
},
{
path: "/sign-up",
element: <Register />,
},
{
path: "/",
element: <PrivateRoute />,
children: [
{
path: "/",
element: <GroupMenu />,
children: [
{
path: "/add-group",
element: <AddGroupModal />,
},
],
},
{
path: "/group-content",
element: <GroupContent />,
},
{
path: "/dashboard",
element: <Dashboard />,
},
],
},
],
},
],
{ basename: "/thatswhy_items_counter/" }
);
return (
<React.StrictMode>
<ThemeProvider theme={theme}>
<RouterProvider router={router} />
</ThemeProvider>
</React.StrictMode>
);
};
如果您需要更多详细信息,这里是 GitHub 上的项目存储库:repo-link 感谢大家提前的帮助!
我尝试将登录功能从自定义登录挂钩移至 ContextProvider 组件。我尝试管理 PrivateRoute 组件中的条件加载状态。我还尝试在 ContextProvider 组件内的登录函数中更新我的当前用户状态。不幸的是,它对我没有帮助。我认为我遇到这个问题是因为我的登录函数需要更多时间来登录用户,而我的handleLogIn函数不会等待。
您已在 ReactTree 中声明了
router
,因此当组件因任何原因重新渲染时,router
会被重新声明并卸载旧路由树并安装新路由树,这会中断任何活动的导航操作。
将
router
声明移出 ReactTree。
const router = createBrowserRouter([
{
path: "/",
element: <App />,
children: [
{
path: "/sign-in",
element: <LogIn />,
},
{
path: "/sign-up",
element: <Register />,
},
{
element: <PrivateRoute />,
children: [
{
path: "/",
element: <GroupMenu />,
children: [
{
path: "/add-group",
element: <AddGroupModal />,
},
],
},
{
path: "/group-content",
element: <GroupContent />,
},
{
path: "/dashboard",
element: <Dashboard />,
},
],
},
],
},
]);
const Main = () => {
return (
<React.StrictMode>
<ThemeProvider theme={theme}>
<RouterProvider router={router} />
</ThemeProvider>
</React.StrictMode>
);
};
或者记住它,以便可以将其作为稳定的参考。
const Main = () => {
const router = useMemo(createBrowserRouter([
{
path: "/",
element: <App />,
children: [
{
path: "/sign-in",
element: <LogIn />,
},
{
path: "/sign-up",
element: <Register />,
},
{
element: <PrivateRoute />,
children: [
{
path: "/",
element: <GroupMenu />,
children: [
{
path: "/add-group",
element: <AddGroupModal />,
},
],
},
{
path: "/group-content",
element: <GroupContent />,
},
{
path: "/dashboard",
element: <Dashboard />,
},
],
},
],
},
]), []);
return (
<React.StrictMode>
<ThemeProvider theme={theme}>
<RouterProvider router={router} />
</ThemeProvider>
</React.StrictMode>
);
};
我建议还将您的
handleLogIn
回调转换为 async
函数,以便您可以 await
logIn
来解析 和 更好地管理 loading
状态,即设置 loading
false 仅在登录尝试成功或失败后。
const handleLogIn = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
if (!/^[a-zA-Z\s].*@.*$/.test(logInValues.email)) {
return setLogInError("email is not correct");
}
if (logInValues.password === "") {
return setLogInError("password field can not be empty");
}
try {
setLogInError("");
setLoading(true);
await logIn(logInValues.email, logInValues.password);
navigate("/");
} catch (errors) {
setLogInError("Failed to log-in in account");
} finally {
setLoading(false);
}
};