#reactjs #aws-amplify #amplify
Вопрос:
Я создаю компонент ProtectedRoute в React, который будет использовать переменную состояния пользователя в качестве опоры. Этот пользователь из моей функции CheckUser (), использующей функцию Auth.currentAuthenticatedUser () от amplify.
function App() {
const [user, setUser] = useState();
const { Auth, Hub } = useContext(AmplifyContext)
async function checkUser() {
try {
const loggedInUser = await Auth.currentAuthenticatedUser();
setUser(loggedInUser);
console.log(loggedInUser);
// get null first time?
} catch(e) {
setUser(null);
console.log(e.message);
}
}
useEffect(() => {
checkUser();
}, [Auth])
return (
<Router>
<Suspense fallback={<p>...loading...</p>}>
<Switch>
<IsUserLoggedIn user={user} loggedInPath={ROUTES.DASHBOARD} path={ROUTES.LOGIN}>
<Route path={ROUTES.LOGIN} component={Login} />
</IsUserLoggedIn>
<IsUserLoggedIn user={user} loggedInPath={ROUTES.DASHBOARD} path={ROUTES.SIGN_UP}>
<Route path={ROUTES.SIGN_UP} component={SignUp} />
</IsUserLoggedIn>
<ProtectedRoute user={user} path={ROUTES.DASHBOARD} exact>
<Route path={ROUTES.DASHBOARD} exact component={Dashboard} />
</ProtectedRoute>
<Route path={ROUTES.RESET_PW} component={ResetPw} />
<Route component={NoPage} />
</Switch>
</Suspense>
</Router>
);
}
// Protected Route Component
import { Route, Redirect } from "react-router-dom";
import * as ROUTES from '../constants/routes';
export default function ProtectedRoute({user, children, ...restProps}) {
console.log(user);
return (
<Route
{...restProps}
render={({location}) => {
if(user) {
return children;
}
if(!user) {
return (
<Redirect
to={{
pathname: ROUTES.LOGIN,
state: { from: location }
}}
/>
)
}
return null;
}}
/>
)
}
// login component
import { useState, useContext } from "react";
import { Link } from "react-router-dom";
import { useHistory } from 'react-router';
import AmplifyContext from "../context/amplify";
import * as ROUTES from '../constants/routes';
export default function Login() {
const { Auth, Hub } = useContext(AmplifyContext);
const history = useHistory();
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState('');
const invalid = !username || !password;
const handleLogin = async (e) => {
e.preventDefault();
try {
// amplify Auth login
await Auth.signIn(username, password);
history.push(ROUTES.DASHBOARD);
console.log('logged in');
} catch(e) {
setError(e.message);
setPassword('');
}
}
return (
<div className="auth-container">
<h2 className="auth-title">Log In</h2>
<div className="login-form-container">
<form className="form login-form" onSubmit={handleLogin}>
<input autoFocus type="text" placeholder="username" aria-label="username" value={username} onChange={({target}) => setUsername(target.value)} />
<input type="password" placeholder="password" aria-label="password" value={password} onChange={({target}) => setPassword(target.value)} />
{error amp;amp; (<p style={{color: 'red'}}>{error}</p>)}
<div className="form-action-container">
<div className="button-container">
<button disabled={invalid} className="form-button" type='submit'>Log In</button>
<p>Need an Account? <span><Link to={ROUTES.SIGN_UP}>Sign Up</Link></span></p>
</div>
<p>Forget your password? <span><Link to={ROUTES.RESET_PW}>Reset</Link></span></p>
</div>
</form>
</div>
</div>
)
}
Текущая проблема заключается в том, что эффект использования (или , может быть, метод аутентификации?) Не обновляет состояние, и поэтому при первом нажатии кнопки «войти» в моем компоненте входа в систему он возвращает значение «null» из моего компонента protectedRoute, а также основного компонента приложения console.log(user)
, возвращая значение null. Только после того, как я обновлю, он изменится и позволит мне получить журнал пользователя, а также перенаправить его в защищенный маршрут.
Это также верно для моего сценария выхода из системы.
export default function Dashboard() {
const { Auth, Hub } = useContext(AmplifyContext);
const history = useHistory();
const handleLogOut = async (e) => {
e.preventDefault();
// amplify call to sign out
await Auth.signOut();
history.push(ROUTES.LOGIN);
}
return (
<div className="dashboard-container">
<h1>Welcome </h1>
<button onClick={handleLogOut}>log out</button>
</div>
)
}
Я не выхожу из системы и не перенаправляюсь, пока не перезагрузю страницу.
Почему методы Auth.SignOut() и Auth.currentAuthenticatedUser() не выполняются так, как я хочу?
Комментарии:
1. Я прочитал об усилении пользовательского потока аутентификации в руководствах ( docs.amplify.aws/руководства/аутентификация/пользовательский поток аутентификации/q/… ), но мне было интересно, можно ли сделать это по-другому? (передача данных о состоянии пользователя моим помощникам по маршруту)
Ответ №1:
это немного улучшило ее работу после того, как она поместила все состояние, связанное с аутентификацией, в поставщика контекста и обернула его вокруг всех компонентов {children}, а затем использовала концентратор для прослушивания изменений для выхода из системы. (Мне пришлось прекратить использовать мои вспомогательные функции маршрута, так что это своего рода облом. Но в данный момент это работает. Я сохраню это в качестве решения). Мне пришлось использовать руководство по усилению для пользовательской аутентификации, поэтому я все еще не очень доволен…
Каждая связанная переменная состояния, которую необходимо использовать в каждом компоненте, использует ее из контекста (опущено для длины).
// storing all state related to Authorization
import { createContext, useContext, useState } from "react";
import AmplifyContext from "./amplify";
const AuthContext = createContext();
function AuthContextProvider({ children }) {
const [formType, setFormType] = useState("signUp");
const [fullName, setFullName] = useState("");
const [username, setUsername] = useState("");
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [authCode, setAuthCode] = useState("");
const [error, setError] = useState("");
const [user, setUser] = useState(null);
const { Auth } = useContext(AmplifyContext);
let invalid;
const checkUser = async () => {
try {
const loggedInUser = await Auth.currentAuthenticatedUser();
setUser(loggedInUser);
console.log(user);
if (user) {
setFormType("dashboard");
} else {
setUser(null);
setFormType("login");
}
} catch (e) {
console.log(e.message);
}
};
const handleSignUp = async (e) => {
e.preventDefault();
try {
// amp auth signup. attribute must match (ie: if email is needed, state var needs to be called email (not other name))
await Auth.signUp({ username, password, attributes: { email } });
console.log("signed up");
setFullName("");
setUsername("");
setEmail("");
setPassword("");
setFormType("confirmSignUp");
} catch (e) {
console.log(e.message);
setError(e.message);
}
};
const handleConfirmAuthCode = async (e) => {
e.preventDefault();
try {
// amp auth confirm sign up
await Auth.confirmSignUp(username, authCode);
setFormType("login");
} catch (e) {
console.log(e.message);
setError(e.message);
}
};
const handleLogin = async (e) => {
e.preventDefault();
try {
// amplify Auth login
await Auth.signIn(username, password);
setUsername("");
setPassword("");
console.log("logged in");
setFormType("dashboard");
} catch (e) {
setError(e.message);
setPassword("");
}
};
const handleLogOut = async (e) => {
e.preventDefault();
// amplify call to sign out
await Auth.signOut();
//set some loading or redirect?
};
return (
<AuthContext.Provider
value={{
error,
setError,
handleSignUp,
checkUser,
handleConfirmAuthCode,
handleLogin,
handleLogOut,
fullName,
setFullName,
username,
setUsername,
email,
setEmail,
password,
setPassword,
formType,
setFormType,
authCode,
setAuthCode,
invalid,
user,
setUser,
}}
>
{children}
</AuthContext.Provider>
);
}
export { AuthContextProvider, AuthContext };
// top
ReactDOM.render(
<AmplifyContext.Provider value={{ Auth, Hub }}>
<AuthContextProvider>
<App />
</AuthContextProvider>
</AmplifyContext.Provider>,
document.getElementById("root")
);
// inside the App component (not yet finished)
import { useContext, useEffect } from "react";
import AmplifyContext from "./context/amplify";
import { AuthContext } from "./context/AuthContext";
import ConfirmSignUp from "./pages/confirmSignUp";
import Login from "./pages/login";
import SignUp from "./pages/sign-up";
import Dashboard from "./pages/dashboard";
import ResetPass from "./pages/reset-pw";
function App() {
const { Hub } = useContext(AmplifyContext);
const {
formType,
setFormType,
username,
setUsername,
error,
setError,
checkUser,
handleLogOut,
} = useContext(AuthContext);
async function setAuthListener() {
Hub.listen("auth", (data) => {
switch (data.payload.event) {
case "signIn":
console.log(`${username} signed in`);
break;
case "signOut":
console.log("user signed out");
setFormType("login");
break;
default:
break;
}
});
}
useEffect(() => {
checkUser();
setAuthListener();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<>
{formType === "signUp" amp;amp; <SignUp />}
{formType === "confirmSignUp" amp;amp; <ConfirmSignUp />}
{formType === "login" amp;amp; <Login />}
{formType === "dashboard" amp;amp; (
<Dashboard handleLogOut={handleLogOut} />
)}
{formType === "reset" amp;amp; (
<ResetPass />
)}
</>
);
}
export default App;