facing issue in redirect user to home page react redux toolkit - reactjs

Hello guys good day to you
I'm a beginner I'm facing one problem below screen shots I'm using redux toolkit with react Js problem is that I want when my user successfully login so its automatic redirect to home page for this I create a state in redux which name is_Login on successfully login I set my state to true and check if its true then user redirect to home its works but is_login state changes 6 times depends on other data coming so my page refresh 6 times how I can solve this??
main file
redux file
main file code
const { is_Login } = useSelector((state) => state);
const navigate = useNavigate();
const [checkIsLogin, setCheckIsLogin] = useState(false);
// if(is_Login){
// setCheckIsLogin(true)
// }
console.log('is_Login', is_Login)
const isLoginCheck = () => {
if (is_Login) {
setCheckIsLogin(true)
}
else {
console.log('not login')
}
}
useEffect(() => {
isLoginCheck()
return console.log('hello from effect')
}, [is_Login])
console.log('checkIsLogin', checkIsLogin)
I'm trying when user login its redirect to home page

Related

Firebase Authentication long loading time to verify user | Nextjs

I have a react application using firebase authentication. I have a global context that handles all functionality around the currently logged in user. This is my useEffect in said context:
useEffect(() => {
const unsubscribeAuth = firebase.auth.onAuthStateChanged(handleUser);
const unsubscribeToken = firebase.auth.onIdTokenChanged(handleUser);
return () => {
unsubscribeAuth();
unsubscribeToken();
};
}, []);
I have a <ProtectedRoute/> component that checks if the user is logged in. When I visit a protected route it's loading for quite some time, around 5 seconds before rendering the actual page.
ProtectedRoute component
function ProtectedRoute({ children }: Props) {
const router = useRouter();
const auth = useAuth();
if (auth.loading) { // Still loading (around 5 seconds!)
return ( <Spinner/>)
}
if (auth.user) {
// Accepted -> render children
return children;
} else {
// Redirect to sign in
router.push("/sign-in");
return null;
}
}
Is there any way to make this check way faster?
It takes around 0.5s if the user has not been logged in before, so I assume the reason it takes a long time is that firebase is confirming the saved token by making a request somewhere. But it shouldn't take that long, right?
I remember that I ran to the same problem and it was because of trying to get the token while the user is not authentificated yet. If it's the case you should verify first if the user is authentificated then try to recieve the token.

React Native: Run actions after succesfully logged in

I'm creating a React Native app in which some actions like adding to favorites require the user to be logged in.
The problem
If a certain action needs authentication, the following flow is executed:
User tab over the favorite button(Protected action)
A modal(screen with presentation: "modal") is rendered to allow the user to enter their credentials
If the user is successfully logged in, the modal is closed and the user is directed to the screen on which it was located(goBack() navigation action).
THE PROBLEM: user needs to press again over the favorite button, the idea is, if the user is successfully logged in, the action (add to favorites) is executed immediately without the user having to perform the action again.
NOTE: I can have different protected actions on the same screen
Question
how can I request the user to log in to perform the action and have the action executed automatically after successful login?
execute the protected action only once, only when the user logs in successfully and the modal is closed, if the user is already authenticated the protected action should not be executed again.
Example flow
function FavoriteScreen({ navigation }) {
const { isAuthenticated } = useAuth();
if (isAuthenticated) {
addFavorite(productId);
} else {
navigation.navigate("LoginScreen");
}
}
Things I've tried
Send a callback through the parameters(called nexAction) from the protected action screen to the login screen, run the callback after successfully log in, and close the modal, but I'm getting non-serializable warning, and this screen implements deep linking, so I cannot ignore the warning as the documentation suggests.
if (isAuthenticated) {
addFavorite();
} else {
navigation.navigate(NavigationScreens.LoginScreen, {
nextAction: addFavorite,
});
}
Use the focus event to run the protected action after the user is successfully logged in and the modal is closed. This approach has some problems, each time user focuses on the screen and is authenticated, the protected action is going to be run, and there may be more than one protected action on the screen, meaning that they will all be executed when the screen is focused.
useEffect(() => {
const unsubscribe = navigation.addListener('focus', () => {
if (isAuthenticated) {
addFavorite();
}
});
return unsubscribe;
}, [isAuthenticated, navigation, addFavorite]);
I think you're on the right path with useEffect. What about storing the user action when clicking a protected action, and then performing the action and clearing the stored value after the user has logged in?
Something like:
const [pendingAction, setPendingAction] = useState();
user clicks addFavorite action, but is not logged in:
setPendingAction({type: 'addFavorite', value: 12});
Then handling the login in the modal, the previous screen will stay mounted and keep its state.
And then calling the pendingAction in the useEffect:
useEffect(() => {
if (isAuth) {
if (pendingAction) {
switch(pendingAction.type) {
// do something
}
setPendingAction(undefined);
}
} else {
// code runs when auth changes to false
}
}, [isAuth, pendingAction, setPendingAction]);
Have you tried an effect?
// Will run once on mount and again when auth state changes
useEffect(() => {
if (isAuth) {
// code runs when auth changes from false to true
} else {
// code runs when auth changes to false
}
}, [isAuth]);
You can't send a function on nav params, but you can send data. You can try parroting the data back from the login screen and performing your one time action like this:
const Fav = ({ route }) => {
const { isAuthenticated } = useAuth();
const {nextAction} = route.params;
useEffect(() => {
if (isAuthenticated) {
// If login navigated back to us with a nextAction, complete it now
if (nextAction === CallbackActions.ADD_FAVORITE) addFavorite();
} else {
navigation.navigate(NavigationScreens.LoginScreen, {
nextAction: CallbackActions.ADD_FAVORITE, // Send some data to the login screen
});
}
}, []);
};
const Login = ({ route }) => {
const {nextAction} = route.params;
// Do login stuff
login().then(() => {
navigation.navigate(NavigationScreens.FavoriteScreen, {
nextAction, // Send back the caller's nextAction
});
});
};

Firebase auth not persisting on page refresh

For some reason when I refresh the page the authentication doesn't persist and itll redirect the user back to sign in, I've seen a couple other postings about this on here but still doesn't work in my case. I'm using nextjs and I have localhost added to my signin method on firebase, it won't let me add localhost:3000. I don't want the user to be redirected to the sign in I want it to be persisted, so however many times the user refreshes the page it stays the same. I'm using nextjs, Here is my code:
useEffect(() => {
firebase.auth().onAuthStateChanged((user) => {
if (user) {
const userDetails = fetchDetails(user.uid);
userDetails.then((use) => setUser(use.data()));
} else {
router.push("/signin");
}
});
}, []);
I've also tried it just like this too:
firebase.auth().onAuthStateChanged((user) => {
if (user) {
const userDetails = fetchDetails(user.uid);
userDetails.then((use) => setUser(use.data()));
} else {
router.push("/signin");
}
});

How can I stay the user in the same page?

Every time I reload the my account page, it will go to the log in page for a while and will directed to the Logged in Homepage. How can I stay on the same even after refreshing the page?
I'm just practicing reactjs and I think this is the code that's causing this redirecting to log-in then to home
//if the currentUser is signed in in the application
export const getCurrentUser = () => {
return new Promise((resolve, reject) => {
const unsubscribe = auth.onAuthStateChanged(userAuth => {
unsubscribe();
resolve(userAuth); //this tell us if the user is signed in with the application or not
}, reject);
})
};
.....
import {useEffect} from 'react';
import { useSelector } from 'react-redux';
const mapState = ({ user }) => ({
currentUser: user.currentUser
});
//custom hook
const useAuth = props => {
//get that value, if the current user is null, meaning the user is not logged in
// if they want to access the page, they need to be redirected in a way to log in
const { currentUser } = useSelector(mapState);
useEffect(() => {
//checks if the current user is null
if(!currentUser){
//redirect the user to the log in page
//we have access to history because of withRoute in withAuth.js
props.history.push('/login');
}
// eslint-disable-next-line react-hooks/exhaustive-deps
},[currentUser]); //whenever currentUser changes, it will run this code
return currentUser;
};
export default useAuth;
You can make use of local storage as previously mentioned in the comments:
When user logs in
localStorage.setItem('currentUserLogged', true);
And before if(!currentUser)
var currentUser = localStorage.getItem('currentUserLogged');
Please have a look into the following example
Otherwise I recommend you to take a look into Redux Subscribers where you can persist states like so:
store.subscribe(() => {
// store state
})
There are two ways through which you can authenticate your application by using local storage.
The first one is :
set a token value in local storage at the time of logging into your application
localStorage.setItem("auth_token", "any random generated token or any value");
you can use the componentDidMount() method. This method runs on the mounting of any component. you can check here if the value stored in local storage is present or not if it is present it means the user is logged in and can access your page and if not you can redirect the user to the login page.
componentDidMount = () => { if(!localStorage.getItem("auth_token")){ // redirect to login page } }
The second option to authenticate your application by making guards. You can create auth-guards and integrate those guards on your routes. Those guards will check the requirement before rendering each route. It will make your code clean and you do not need to put auth check on every component like the first option.
There are many other ways too for eg. if you are using redux you can use Persist storage or redux store for storing the value but more secure and easy to use.

Reactjs SPA - Pressing IOS Safari back button updates the url but not the component view

We have a reactjs SPA application where we want to navigate back and forth using browser back button/swipe the screen, if on Mac.
when a user clicks on a link from home page, they will be navigated to a detail page and the url looks like https://example.com/pdetail?prdt=Computer
On detail page, User has an ability to search, and when the user searches(Say Stationery), we update the url and push the url to history and the the detail page component is updated with the searched data.Like
https://example.com/pdetail?prdt=Stationery
filterSearch(searchval) {
//search related code to set variables
let newUrl = setParams(searchval, 'prdt')
this.props.history.push(`?${newUrl}`);
// dispatch an api call to get data
}
export function setParams(query = "", param) {
const searchParams = new URLSearchParams(window.location.search);
searchParams.set(param, query.trim());
return searchParams.toString();
}
Now when the user browse back using the browser back button, we expect it to go the previous data(https://example.com/pdetail?prdt=Computer) from (https://example.com/pdetail?prdt=Stationery) and Vice Versa as we move back and forth. It is working fine in chrome and IE.
But in Safari, as the user presses back button, url is changing in the browser but the component view is not.
as we navigate back, I noticed that it did not go to ComponentDidMount as we go to the same page. But it goes ComponentDidUpdate so I added my code in there.
componentDidUpdate() {
window.onpopstate = (e) => {
if(this.props.history.action == 'POP')
{
e.preventDefault();
const search = window.location.search;
const params = new URLSearchParams(search);
if(params.has('prdt') === true)
{
const selectedprdt = params.get('prdt');
this.props.fetchDetails('FETCH_PRDT_DETAILS' , selectedprdt);
}
}
}
}
Let me know how can get this back forth page navigation with consistent url and data in Safari.
It is working fine in IE and chrome.
EDIT:
I have edited my code to add an eventListener in componentDidMount and UI is working in Safari. But when I see in the logs, I noticed the event runs multiple times not just once, which is very inefficient as we are making server calls.
componentDidMount()
{
const selectedprdt = getParams(window.location, 'prdt');
if(selectedprdt !== '')
{
this.props.fetchDetails('FETCH_PRDT_DETAILS' , selectedprdt);
}
window.addEventListener('popstate', this.handleBackButton)
}
handleBackButton = (e) =>
{
if(this.props.history.action == 'POP')
{
const search = window.location.search;
const params = new URLSearchParams(search);
if(params.has('prdt') === true)
{
const selectedMarket = params.get('prdt');
this.props.fetchDetails('FETCH_PRDT_DETAILS' , selectedprdt);
}
e.preventDefault();
}
}
Please let me know how to stop eventListener to executing the calls multiple times.
Thanks,
I had to remove the same listener on component Unmount.
componentWillUnMount()
{
window.removeEventListener('popstate', this.handleBackButton)
}

Resources