I try to do RequierAuth lock to protect against an unauthorized user. First created the context where auth should accept the token:
AuthProvider.js:
const AuthContext = createContext({});
export const AuthProvider = ({ children }) => {
const [auth, setAuth] = useState({});
return (
<AuthContext.Provider value={{ auth, setAuth }}>
{children}
</AuthContext.Provider>
)
}
export default AuthContext;
Next created a hook for the above context:
useAuth.js
const useAuth = () => {
return useContext(AuthContext);
}
export default useAuth;
Next, the actual "lock" for protection, where I check if there is a token and return either the page or send the user to the login page:
RequierAuth.js
const RequierAuth = () => {
const {auth} = useAuth();
const location = useLocation();
return (
auth.token?<Outlet/> : <Navigate to = "/auth" state = {{from:location}} replace />
);
}
export default RequierAuth;
App.js
function App() {
return (
<>
...
<div className="App">
<BrowserRouter>
<Routes>
<Route path="/" element={<Layout />}>
<Route path="auth" element={<Login />} />
<Route path="reg" element={<Register />} />
<Route element = {<RequierAuth/>}>
<Route path="home" element={<Home />} />
</Route>
</Route>
</Routes>
</BrowserRouter>
</div>
</>
);
}
export default App;
index.js
root.render(
<React.StrictMode>
<AuthProvider>
<App />
</AuthProvider>
</React.StrictMode>
);
And actually the question is that now when I call setAuth on the login page:
LoginForm.js
const Login = () =>{
const {auth,setAuth} = useAuth();
const [authResponce,setAuthResponce] = useState(null);
const [login,setLogin] = useState("");
const onChangeLogin = (e) => {
e.preventDefault();
const username = e.target.value;
setLogin(username);
};
const [password,setPassword] = useState("");
const onChangePassword = (e) => {
const password = e.target.value;
setPassword(password);
};
const instance = axios.create({
baseURL: "http://localhost:8080",
headers: {
"Content-Type": "application/json",
},
});
const postUser = async (user) =>{
return instance.post("http://localhost:8080/auth", user);
}
const onLogin = (e) =>{
e.preventDefault();
const user = {
login: login,
password: password,
};
(async() => {
const response = await postUser(user);
const data = response.data;
console.log(data);
const token = data.token;
console.log(token);
setAuth({token});
console.log(auth);
console.log(auth.token);
})();
};
return (
<div className="Auth-form-container">
<form className="Auth-form" onSubmit={onLogin}>
<div className="Auth-form-content">
<h3 className="Auth-form-title">Sign In</h3>
<div className="form-group mt-3">
<label>Login</label>
<input
type="login"
className="form-control mt-1"
placeholder="Enter login"
value={login}
onChange={onChangeLogin}
/>
</div>
<div className="form-group mt-3">
<label>Password</label>
<input
type="password"
className="form-control mt-1"
placeholder="Enter password"
value={password}
onChange={onChangePassword}
/>
</div>
<div className="d-grid gap-2 mt-3">
<button type="submit" className="btn btn-primary">
Submit
</button>
</div>
</div>
</form>
</div>
)
};
export default Login;
First, if you get a token for the first time, then why is the value not printed in the console, if you get it again on the form, it already prints the expected value, but when you switch to home, it sends it back to the login.
I set breakpoint and at the moment of checking auth.token? indicates that the value is not set, although setAuth has set the value.
The check itself seems to work, if you put the default value in auth and try to compare with it, then we will normally get to /home.
I've only recently started studying and I can't figure out what the error is, so I'll be glad for help to figure it out.
First, updating a state is an "asynchronous" task. A re-render is needed in order to have the updated value. Which is why you are not seeing change with those lines that you have inside onLogin:
setAuth({token});
console.log(auth);
Second, after the login process, you said in the comments that you are using the browser to redirect to /home. Well, you should know that doing so refreshes the page, so all your states come to their initial values, so auth would be {}. This is why it's redirecting to "/auth".
You should use React Router Dom's redirection mechanism, useNavigate for example. Change LoginForm.js slightly, like so (I added comments in the code):
import { useEffect } from "react"; // line to add
import { useNavigate } from "react-router-dom"; // line to add
const Login = () => {
const navigate = useNavigate(); // line to add
const { auth, setAuth } = useAuth();
const [authResponce, setAuthResponce] = useState(null);
const [login, setLogin] = useState("");
const onChangeLogin = (e) => {
e.preventDefault();
const username = e.target.value;
setLogin(username);
};
const [password, setPassword] = useState("");
const onChangePassword = (e) => {
const password = e.target.value;
setPassword(password);
};
const instance = axios.create({
baseURL: "http://localhost:8080",
headers: {
"Content-Type": "application/json",
},
});
const postUser = async (user) => {
return instance.post("http://localhost:8080/auth", user);
};
// useEffect to add
useEffect(() => {
if (auth) {
console.log(auth); // add your logs here to see the updates after re-render
navigate("/home");
}
}, [auth]);
const onLogin = (e) => {
e.preventDefault();
const user = {
login: login,
password: password,
};
(async () => {
const response = await postUser(user);
const data = response.data;
console.log(data);
const token = data.token;
console.log(token);
setAuth({ token });
})();
};
return (
<div className="Auth-form-container">
<form className="Auth-form" onSubmit={onLogin}>
<div className="Auth-form-content">
<h3 className="Auth-form-title">Sign In</h3>
<div className="form-group mt-3">
<label>Login</label>
<input
type="login"
className="form-control mt-1"
placeholder="Enter login"
value={login}
onChange={onChangeLogin}
/>
</div>
<div className="form-group mt-3">
<label>Password</label>
<input
type="password"
className="form-control mt-1"
placeholder="Enter password"
value={password}
onChange={onChangePassword}
/>
</div>
<div className="d-grid gap-2 mt-3">
<button type="submit" className="btn btn-primary">
Submit
</button>
</div>
</div>
</form>
</div>
);
};
export default Login;
The above solution works, but if you refresh the page manually, there is no way to remember that a user has been logged in. If you want that feature, you can use localStorage.
For that, change the useEffect I added inside LoginForm.js to the below code:
useEffect(() => {
if (auth) {
console.log(auth); // add your logs here to see the updates after re-render
localStorage.setItem("token", auth.token); // so you get it later
navigate("/home");
}
}, [auth]);
Change AuthProvider.js so you get the token form localStorage if there is one:
const AuthContext = createContext({});
export const AuthProvider = ({ children }) => {
const [auth, setAuth] = useState({ token: localStorage.getItem("token") });
return <AuthContext.Provider value={{ auth, setAuth }}>{children}</AuthContext.Provider>;
};
export default AuthContext;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With