我正在尝试制作一个反应应用程序,我想进行一些登录,然后可以访问主站点。问题是它根本没有真正发挥作用。使用正确的密码首次登录时,不会加载私人页面。但如果我然后返回并只需单击登录而不输入密码或登录,它就会加载正常。如果输入了错误的密码,它也不会重定向。
这个应用程序是在 next.js 中的 React 和 TypeScript 中构建的,但我无法使用任何服务器端渲染,因为这是在静态站点上进行的。
这是登录页面代码
'use client'
import React, {useState} from 'react';
import { AuthProvider, useAuth } from './AuthContext';
import { useNavigate } from 'react-router-dom';
import './index.css';
const LoginComp: React.FC = () => {
const navigate = useNavigate();
const { signin, isAuthenticated } = useAuth();
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const handleLogin = () => {
try{
signin(username, password);
navigate('/test');
}
catch{
navigate('/');
}
};
return (
<div className="container">
<div className="content">
<form id='form'>
<img src='picture url' width='100%' alt='Logo' /><br />
<input id="uname" type="username" placeholder="Username" value={username} onChange={(e) => setUsername(e.target.value)} /><br />
<input id="pword" type="password" placeholder="Password" value={password} onChange={(e) => setPassword(e.target.value)} /><br />
<p id="error"></p>
<button id="submit" onClick={handleLogin}>Login</button>
</form>
</div>
</div>
);
};
const LoginPage: React.FC = () => {
return(
<AuthProvider>
<LoginComp />
</AuthProvider>
);
}
export default LoginPage;
这是路由文件
'use client'
import React from 'react';
import { BrowserRouter, Route, Routes, HashRouter } from 'react-router-dom';
import PrivateRoutes from './PrivateRoutes';
import LoginPage from './login';
import PrivatePage from './home';
const App: React.FC = () => {
return (
<BrowserRouter >
<Routes>
<Route element={<LoginPage />} path="/"/>
<Route element={<PrivateRoutes />}>
<Route element={<PrivatePage comp_name='/test'/>} path='/test'/>
</Route>
</Routes>
</BrowserRouter>
);
};
export default App;
这是私人页面文件
'use client'
import React from 'react';
import { Navigate, Outlet } from 'react-router-dom';
import { AuthProvider, useAuth } from './AuthContext';
function PrivateRouting() {
const { isAuthenticated } = useAuth();
return (
isAuthenticated ? <Outlet /> : <Navigate to="/test" />
);
}
function PrivateRoutes() {
return(
<AuthProvider>
<PrivateRouting />
</AuthProvider>
);
};
export default PrivateRoutes;
这是我的身份验证文件
'use client'
import React, { createContext, useContext, useState } from 'react';
import Cookies from 'js-cookie';
interface AuthContextType {
isAuthenticated: boolean;
signin: (username: string, password: string) => Promise<void>; // Update signin function to return a promise
signout: () => void;
}
const AuthContext = createContext<AuthContextType | undefined>(undefined);
export const useAuth = () => {
const context = useContext(AuthContext);
if (!context) {
throw new Error('useAuth must be used within an AuthProvider');
}
return context;
};
interface AuthProviderProps {
children: React.ReactNode; // Define children prop
}
export const AuthProvider: React.FC<AuthProviderProps> = ({ children }) => {
const [isAuthenticated, setIsAuthenticated] = useState<boolean>(() => {
// Check cookies for authentication token
return !!Cookies.get('token');
});
const signin = async (username: string, password: string) => {
try {
const formData = new FormData();
formData.append('uname', username);
formData.append('pword', password);
const response = await fetch('https link to my public domain', {
method: 'POST',
body: formData,
});
if (!response.ok) {
throw new Error('Authentication failed');
}
const data = await response.json();
const token = data.token;
const expiry = data.expiry;
// Check if token and expiry exist
if (!token || !expiry) {
throw new Error('Token or expiry not received');
}
// Store the token in cookies with the provided expiry time
const expirationTime = new Date();
expirationTime.setMinutes(expirationTime.getMinutes() + 2);
Cookies.set('token', token, { expires: expirationTime });
setIsAuthenticated(true);
} catch (error) {
console.error('Signin failed:', error);
throw error;
}
};
const signout = () => {
// Remove token from cookies
Cookies.remove('token');
setIsAuthenticated(false);
};
return (
<AuthContext.Provider value={{ isAuthenticated, signin, signout }}>
{children}
</AuthContext.Provider>
);
};
每次我做出一些改变,它都会破坏更多。
我对 React 很陌生,并且有一定的 Web 开发经验,因此任何有关 React 的一般提示也将不胜感激。
登录组件中的
handleLogin
函数调用 signin
方法,但在导航之前不会等待 signin
完成。由于 signin
是一个异步函数,因此您应该在导航之前等待其完成。
const handleLogin = async (e) => {
e.preventDefault(); // Prevent default form submission
try {
await signin(username, password);
navigate('/test');
} catch {
navigate('/');
}
};
您还使用
useNavigate
中的 react-router-dom
,它不是 Next.js 的一部分。在 Next.js 中,您应该使用 useRouter
中的 next/navigation
进行导航,
import { useRouter } from 'next/navigation';
// Inside your component
const router = useRouter();
// To navigate
router.push('/test');
在您的
PrivateRouting
组件中,您将未经身份验证的用户重定向到 /test
,这似乎是您的受保护路由。如果他们没有经过身份验证,您应该将他们重定向到登录页面 (/
),
return (
isAuthenticated ? <Outlet /> : <Navigate to="/" />
);