react-router-v4 not redirecting properly - reactjs

I have got the url to change to the url I want it to, but the only way I can get it to work is by refreshing the page and then it goes to the url.
An example is lets say I am on localhost:3000/signin and when I sign in I want the user to be redirected to the posts page on localhost:3000/posts. When I click the button I get localhost:3000/posts but the page just stays on the signin page. I have to hit refresh for it to go to that URL.
**********
EDIT: I also noticed that when I hit back or forward in the browser that it isn't rendering till I hit refresh also. So this could be some other issue? I am using react-router-v4.
Here is the code I have so far:
This is the on submit function being called when the button is clicked:
onSubmit({email, password}) {
this.props.signinUser({email, password}, () => {
this.props.history.push('/posts');
});
}
this is the action signinUser:
export function signinUser({email, password}, cb) {
return function(dispatch) {
axios.post(`${ROOT_URL}/signin`, {email, password})
.then((response) => {
dispatch({type: AUTH_USER});
console.log(response);
localStorage.setItem('token', response.data.token);
cb();
})
.catch(() => {
dispatch(authError('bad login info'));
})
}
}

can you try this, this should work.
this.history.pushState(null, '/posts');
if you are using browserHistory
this.context.router.push('/posts'); or browserHistory.push('/login');

One way to do it is that somewhere in state you have item "isLoggedin" or something like that. If that item is false, you you render login route normally with edit boxes to enter user info. when isLoggedIn in state changes to true, you render your login route to something like this:
<Redirect to="/posts"/>
and off you go!

Related

Directly redirect to AAD login page on hitting the URL in browser

Using MSAL 2.0 in React we can easily achieve redirect to login page using below methods
Login with pop-up
Login with redirect
But both the methods can be performed on a login/signin button action.
In my case, I want to redirect my React app directly to login page, when the URL is hit in the browser i.e. without button action.
Also, while doing the redirect I also need to pass my multiple scopes, for user consent.
I did some research but didn't found any feasible solutions.
Please can anyone help me with this ?
When an application starts it always start with the app.js. In the app.js class constructor you can check your token if it is valid/invalid and or if expired.
In order to redirect your React app directly to login page try this:
state = {
show: false;
}
componentWillReceiveProps(nextProps) {
if (nextProps.children !== this.props.children) {
this.checkAuth();
}
}
componentDidMount() {
this.checkAuth();
}
checkAuth() {
Api.checkAuth()
.then(result => {
if (result.success) {
this.setState({ show: true });
} else {
return <Redirect to='/login' />
}
});
}

How to redirect user back to initially-requested page after authentication with React-Router v6?

I have a situation where, if a user isn't authenticated, they get redirected to the login page.
Once the user logs in, they get sent back to the main page.
This is a pretty typical situation with React, but I'm wondering how I can redirect the user back to a certain page after they authenticate.
Suppose the user tries to access /app/topics, which would be a private route. They would get redirected to /, where they have to authenticate. Afterwards, they get redirected to /app/about once they authenticated.
How can I ensure that the user gets redirected back to /app/topics instead?
The About component would look something like,
const About = ({ user }) => {
const navigate = useNavigate();
React.useEffect(() => {
if (!user) navigate("/");
}, [user]);
return (
<div>
<h2>About</h2>
</div>
);
};
export default About;
And Home (or the 'login page') would look like this,
const Home = ({ user, setUser }) => {
const navigate = useNavigate();
React.useEffect(() => {
if (user) navigate("/app/about");
}, [user]);
return (
<div>
<h2>Login</h2>
<input />
<button onClick={() => setUser(1)}>login</button>
</div>
);
};
export default Home;
I'm aware the this line,
if (user) navigate("/app/about");
Is why the user gets redirected to About upon authenticating, but I'm wondering how I can change this up, so that the user is redirected back to Topics.
I've tried a combination of different approached. The most promising thing that I've tried was saving the requested uri into my Redux state. This cause issue with my existing code, however.
I've also tried saving state with Navigate or useNavigate.
If I recall correctly, React-Router v5 had a redirect prop or something of the sort, that redirected the user back to a certain page.
I would just fallback to good old query parametr in url, just upon redirecting to login page, put query param, from or something in url, and upon successfull login do the redirect, this has the huge advatage that if you take him to different page or for some reason he has to reload page, he keeps this info.
React router v6 documentation provides an exemple that answers you requirements, here is their sandbox.
They use the from property of location's state:
function LoginPage() {
let navigate = useNavigate();
let location = useLocation();
let auth = useAuth();
let from = location.state?.from?.pathname || "/";
function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
event.preventDefault();
let formData = new FormData(event.currentTarget);
let username = formData.get("username") as string;
auth.signin(username, () => {
// Send them back to the page they tried to visit when they were
// redirected to the login page. Use { replace: true } so we don't create
// another entry in the history stack for the login page. This means that
// when they get to the protected page and click the back button, they
// won't end up back on the login page, which is also really nice for the
// user experience.
navigate(from, { replace: true });
});
}

React Next js app redirect to login is premature

After a lot of searching for several hours, I have the following code to redirect from a user profile page if not logged in.
NOTE: Simply showing a not authorized page is easy but its the redirect thats messing things up.
The code does the job of redirecting when user is not logged in.
const Dashboard = () => {
const [user, { mutate }] = useCurrentUser();
const router = useRouter();
useEffect(() => {
// redirect to login if user is not authenticated
if (!user) router.push('/login');
}, [user]);
...
The problem is when a user is logged in and directly goes to /user/dashboard route, for a split second, user is undefined may be so it redirects to login. When it gets to login, it finds that user is authenticated so redirects to home page because I am redirecting a logged in user to home page.
How to prevent that split second of "not a user" status when page is first loading?
I tried -
getInitialProps
getServerSideProps - Cant use router because next router can only be used on client side
componentDidMount - UseEffectI tried above is the equivalent correct?
Edit: Based on answer below, I tried this but still directly takes user to login first. I am using react cookies and I do see loggedIn cookie as true when user is logged in and its not set when user is not logged in.
Dashboard.getInitialProps = ({ req, res }) => {
console.log(req.headers.cookie)
var get_cookies = function(request) {
var cookies = {};
request.headers && request.headers.cookie.split(';').forEach(function(cookie) {
var parts = cookie.match(/(.*?)=(.*)$/)
cookies[ parts[1].trim() ] = (parts[2] || '').trim();
});
return cookies;
};
//console.log(get_cookies(req)['loggedIn']);
if (get_cookies(req)['loggedIn'] == true) {
console.log("entered logged in")
return {loggedIn: true};
}
else {
console.log("entered not logged in")// can see this on server console log
// User is not logged in, redirect.
if (res) {
// We're on the server.
res.writeHead(301, { Location: '/login' });
res.end();
} else {
// We're on the client.
Router.push('/login');
}
}
}
You can implement redirect when not authenticated in getServerSideProps
Below example is based on JWT Authentication with cookies.
export const getServerSideProps = async (ctx) => {
const cookie = ctx.req.headers.cookie;
const config = {
headers: {
cookie: cookie ?? null
}
}
let res;
try {
// your isAuthenticated check
const res = await axios('url', config);
return { props: { user: res.data } };
} catch (err) {
console.error(err);
ctx.res.writeHead(302, {
Location: 'redirectUrl'
})
ctx.res.end();
return;
return { props: { user: null } };
}
}
You should be able to use getInitialProps to redirect. You just need to check whether you're on the server or the client and use the proper redirect method. You can't use hooks in getInitialProps so your useCurrentUser approach won't work and you'll need some other way to check whether the user is authed. I don't know anything about the structure of your application, but it's probably just some kind of request to wherever you're storing the session.
import Router from 'next/router';
const Dashboard = (props) => {
// props.user is guaranteed to be available here...
};
Dashboard.getInitialProps = async ({ res }) => {
// Check authentication.
// Await the response so that the redirect doesn't happen prematurely.
const user = await ...
// User is logged in, return the data you need for the page.
if (user) {
return { user };
}
// User is not logged in, redirect.
if (res) {
// We're on the server.
// Make the redirect temporary so it doesn't get cached.
res.writeHead(307, { Location: '/login' });
res.end();
} else {
// We're on the client.
Router.push('/login');
}
};
After many hours of struggle, there was one number that was breaking this.
Instead of
res.writeHead(301, { Location: '/login' });
I used
res.writeHead(307, { Location: '/login' });
and it worked.
301 is a permanent redirect so if we use that, when the user logs in, the browser still holds the redirect cache.
From next js docs
Next.js allows you to specify whether the redirect is permanent or not with the permanent field. This is required unless you need to specify the statusCode manually
When permanent is set to true we use a status code of 308 and also set a Refresh header for backwards compatibility with IE11.
When permanent is set to false we use a status code of 307 which is not cached by browsers and signifies the redirect is temporary.
Next.js permits the following status codes:
-301 Moved `Permanently`
-302 Found
-303 See Other
-307 `Temporary` Redirect
-308 Permanent Redirect

How to update login data into state after changing pathname in reactjs

I'm doing user login and logout and after login or logout i am just changing pathname to homepage using this.props.history.push({pathname: '/', state: { url: this.props.location.pathname }});. Now when i redirect to that page the data is not updating, i have refresh the page to update the data. I am calling below code to update the data.
componentDidMount(){
let data = AuthService.fetchUserObj();
console.log(data)
this.setState({user: data})
}
Please assist me how to automatically update user data to state once pathname changed after login.
It's just an assumption but maybe fetchUser is an asynchronous method. If it's so and method returns a promise then it has to be something like:
componentDidMount(){
AuthService.fetchUserObj().then(data => {
this.setState({user: data});
}
}

Possible reason for unwanted page refresh in componentWillMount

After Login I want to show a license agreement page.
To show the license I used this code
index.js
componentWillMount(){
this.props.actions.user.LicenseAgreement();
}
action.js
export const LicenseAgreement = () => {
return(dispatch) =>{
authService.LicenseAgreement()
.then((response) =>{
window.location.href="/";
dispatch({type: LICENSE_AGREE, payload:response});
})
.catch((error)=>{
notification.error({
message: 'User',
description: error.message,
})
})
}
}
reducer.js
case LICENSE_AGREE: {
return{...state, agreement: action.payload.data.text}
}
The problem is that the page refreshes before clicking Agree button .
But when I commented componentWillMount() there was no refreshing.
So how could I stop refreshing of this page?
Use componentDidMount life cycle and remove window.location.href. Also to avoid refreshing, you can user Router

Resources