Firebase.auth().onstateChanged() not working after clearing browser cache - reactjs

I cleared my browser cache and now my app cant login
export function IsUserRedirect({ user, loggedInPath, children, ...rest}){
return (
<Route
{...rest}
render={() => {
if(!user){
return children;
}
if(user){
return (
<Redirect
to={{
pathname: loggedInPath
}}
/>
)
}
return null;
}}
/>
)
}
export function ProtectedRoute({ user, children, ...rest}){
return(
<Route
{...rest}
render={({location}) => {
if(user){
return children;
}
if(!user){
return (
<Redirect
to={{
pathname: 'signin',
state: { from : location}
}}
/>
)
}
return null;
}}
/>
)
}
I think it stored my login info on the browser as a localstorage but after clearing it still recognizes it as the user is logged in and takes me to the next page.
But on the next page i have kept a loading state for getting user data, as it doesnt has any user it just keeps loading and goes nowhere. can someone help
export default function useAuthListener(){
const [user, setUser] = useState(JSON.parse(localStorage.getItem('authUser')));
const {firebase} = useContext(FirebaseContext);
useEffect(() => {
const listener = firebase.auth().onAuthStateChanged((authUser) => {
if(authUser){
localStorage.setItem('authUser', JSON.stringify(authUser));
setUser(authUser);
}else {
localStorage.removeItem('authUser');
setUser(null);
}
});
return ()=> listener();
}, []);
return { user};
}

just a quick suggestion:
localStorage.clear();
Source:
(Another aproach might be a reboot, to see if it acts differently...)
Greetings,
Alexander

Related

How can I avoid infinite loops in my React Router private routes?

In my App.js, I have some authenticated pages I protect with <PrivateRoute>, like so:
<PrivateRoute path="/dashboard">
<Dashboard />
</PrivateRoute>
I implement <PrivateRoute> like so:
function PrivateRoute({ children, ...rest }) {
return (
<Route {...rest} render={() => <CheckRedirect children={children} />} />
);
}
The problem is, the <CheckRedirect> function calls out to an endpoint on my server which dynamically tells you where to redirect.
Here's the function:
export const CheckRedirect = ({ children }) => {
const [isChecking, setIsChecking] = React.useState(true);
const [target, setTarget] = React.useState(null);
const url = "https://example.com/get-redirect"
useEffect(() =>{
async function getPage() {
axios.get(url)
.then(function (response) {
setTarget(response.data.message)
}).finally(() => setIsChecking(false))
}
getPage();
}, []);
if (isChecking) {
return "... Checking";
}
return {target} ? (
<Redirect to={target} />
) : (
<Redirect to='/404' />
);
};
If you're not logged in, it will send back "/login" in the message field. If you're logged in, it will send "/dashboard".
If it sends back "/dashboard", then React Router produces an infinite loop! It tries the same <PrivateRoute> again, which calls out to the endpoint again, which will once again return "/dashboard", and so on...
Is there a way I can tell my <PrivateRoute> to not do the <CheckRedirect> function if this is already the result of a redirect?
I haven't tested it myself, but have you tried passing path as a prop to CheckRedirect and only do the setTarget in your getPage fetch if it returns a different route?
function PrivateRoute({ children, path, ...rest }) {
return (
<Route {...rest} render={() => <CheckRedirect children={children} path={path} />} />
);
}
export const CheckRedirect = ({ children, path }) => {
const [isChecking, setIsChecking] = React.useState(true);
const [target, setTarget] = React.useState(null);
const url = "https://example.com/get-redirect"
useEffect(() =>{
async function getPage() {
axios.get(url)
.then(function (response) {
const newPath = response.data.message
if (path !== newPath) {
setTarget(newPath)
}
}).finally(() => setIsChecking(false))
}
getPage();
}, []);
if (isChecking) {
return "... Checking";
}
return {target} ? (
<Redirect to={target} />
) : (
<Redirect to='/404' />
);
};
To avoid CheckRedirect to do any redirect if everything is ok (ie. it's a valid request for that route), ensure CheckRedirect actually returns null in that case. If you have control over the server response, I'd return a different value (not null, but -1 for example) for non-existent routes (ie. to redirect to 404), and keep null for when you really just want to return null.
In CheckRedirect component, you don't even use children prop. It renders a string and then redirects to a page. It's normal that it loops forever. Pass path as a prop to CheckRedirect component and if it's same as server response, render the children.
Add path prop and pass it:
export const CheckRedirect = ({ children, path }) => {
Add your conditional before redirecting:
if (target === path) {
return children
}
Just change your PrivateRoute Logic to something like this
const PrivateRoute = ({ component: Component, ...rest }) => {
return (
<Route
{...rest}
render = { props =>
user.isOnline ? ( <Component {...props} /> ) :
(
<Redirect
to={{
pathname: "/login",
state: { from: props.location }
}}
/>
)
}
/>
)
}
then
<PrivateRoute exact path="/dashboard" component={Dashboard} />

React updating during transition error and I am not updating state inside render

I am trying to get rid of this error but cannot seem to find why its triggering. Here is my code for a role based routing component. I call the check to see what the users role is and based on the accepted roles array passed in, if they match then set state for granted true. If no roles are passed in I pass them to the route meaning it was not protected. If no user then send them to login. If they are not granted then I send them home.
import React, { useState, useEffect, useContext } from 'react';
import { Redirect, Route, useHistory } from 'react-router-dom';
import UserContext from '#Context/User';
const RoleRouting = ({
component: Component,layout: Layout, roles, ...rest
}) =>{
const [granted, setGranted] = useState(null);
const { user } = useContext(UserContext);
const history = useHistory();
useEffect(() => {
grantPermission(roles)
}, [roles]);
const grantPermission = (requestedRoles) => {
if(requestedRoles != undefined){
requestedRoles.map(function (role) {
if(user){
if(role === user.is_admin){
setGranted(true);
}else{
setGranted(false);
}
}
});
}else{
setGranted(true);
}
};
return(
<div>
{granted != null &&
<div>
{ granted && user &&
<Route
{...rest} render={props => (
<Layout>
<Component {...props} />
</Layout>
)}
/>
}
{ !granted && user &&
history.push('/')
}
{ !granted && !user &&
<Redirect to="/login" />
}
</div>
}
</div>
)
}
export default RoleRouting;
I changed the flow of the state updating like below and it is still complaining...
const grantPermission = (requestedRoles) => {
let grantFlag = false
if(requestedRoles != undefined){
requestedRoles.map(function (role) {
if(user){
if(role === user.is_admin){
grantFlag = true
}
}else{
grantFlag = false
}
});
}else{
grantFlag = true
}
setGranted(grantFlag)
};

Expo/React Native - Can't redirect to homescreen after login

I am working on a mobile app with Expo/React Native and trying to have a login screen for sign in. Once signed in,the user will be redirected to the homescreen.
However I have not been able to configure it with setting the state upon successful gaining the credentials for "isLoggedIn:true." On the stack navigator, I tried to set it up where it will show different screens (Home and Third Screen) if logged in while the LoginScreen will be if logged in is false. How do I configure it to properly redirect to the homescreen upon successful login.
export default function App() {
const [state, setstate] = useState({
isSignedIn:false,
})
return (
<NavigationContainer>
<Stack.Navigator>
{state.isSignedIn == false ? (
<>
<Stack.Screen name='Login' component={LoginScreen} />
</>
) : (
<>
<Stack.Screen name='Home' component={HomeStack} options={({ route }) => ({
headerTitle: getHeaderTitle(route) })} />
<Stack.Screen name='ThirdScreen' component={ThirdScreen}/>
</>
)}
</Stack.Navigator>
</NavigationContainer>
);
}
Below is my code for the login on the LoginScreen.js:
render() {
function handleSubmit({ props }) {
let formData = new FormData();
formData.append('username',this.state.username);
formData.append('password',this.state.password);
fetch('<My login API from the server side>', {
method: 'POST',
body:formData
}).then(response => {
if(response.status == 200) {
response.body.getReader().read().then(({value, done}) => {
const decoder = new TextDecoder('utf-8')
const keyitems = decoder.decode(value);
//convert to JSON
const obj = JSON.parse(keyitems);
//get individual values from string
this.setState({
userToken:obj.key.toString(),
username:obj.user.username,
user_id:obj.user.id,
user_image:obj.user.userprofile.user_image,
isSignedIn:true
})
//refresh or redirect to hopefully get to home
//assuming the state has been switched to logged
this.props.navigation.navigate('Home')
})
} else {
//issue alert to say try again
console.log("bad")
}
});
}
you don't need to navigate to the Home screen manually by calling navigation.navigate('Home'). React Navigation will automatically navigate to the Home screen when isSignedIn becomes true.
https://reactnavigation.org/docs/auth-flow/
It seems like you have written a hook call 'setstate' but you have not used that in the child component. To make it work, you would need to pass the function argument of the hook in the parent component to the child component and call that to set the signed in state.
Try modifying your code like this
export default function App() {
const [state, setState] = useState({
isSignedIn:false,
})
return (
<NavigationContainer>
<Stack.Navigator>
{state.isSignedIn == false ? (
<>
<Stack.Screen name='Login' component={LoginScreen} setState={setState} />
</>
) : (
<>
<Stack.Screen name='Home' component={HomeStack} options={({ route }) => ({
headerTitle: getHeaderTitle(route) })} />
<Stack.Screen name='ThirdScreen' component={ThirdScreen}/>
</>
)}
</Stack.Navigator>
</NavigationContainer>
);
}
and
render() {
function handleSubmit({ props }) {
let formData = new FormData();
formData.append('username',this.state.username);
formData.append('password',this.state.password);
fetch('<My login API from the server side>', {
method: 'POST',
body:formData
}).then(response => {
if(response.status == 200) {
response.body.getReader().read().then(({value, done}) => {
const decoder = new TextDecoder('utf-8')
const keyitems = decoder.decode(value);
//convert to JSON
const obj = JSON.parse(keyitems);
//get individual values from string
props.setState({
userToken:obj.key.toString(),
username:obj.user.username,
user_id:obj.user.id,
user_image:obj.user.userprofile.user_image,
isSignedIn:true
})
})
} else {
//issue alert to say try again
console.log("bad")
}
});
}

Hide Some Element With Local Storage In React

I want to hide some navigation when the user haven't login yet, so I use local storage to save user's id and use if logic to hide and show the navigation, but when i clear the data in local storage and compare it to null, the navigation still showed up.
Here is the code to save data in local storage
loginUser = () => {
Axios.post('http://private-6fdd31-intern1.apiary-mock.com/interns/login', this.state.user)
.then((res) => {
if(res.data.role === "admin")
{
localStorage.setItem("user", res.data.user_id)
this.props.history.push('/member-list');
}
}, (err) => {
console.log(err);
})
}
This is how I compare and clear the data when logout navigation is clicked
handleLogout = () => {
localStorage.clear("user");
}
render() {
return(
<Router>
<Fragment>
<div className="navigation">
{ localStorage.getItem("user") !== null?
<Fragment>
<Link to="/member-list">Member</Link>
<Link to="/override-list">Override</Link>
<Link onClick={this.handleLogout} to="/">Logout</Link>
</Fragment>
: null
}
</div>
<Route path="/" exact component={routeProps => <Login {...routeProps}/>}/>
<Route path="/member-list" component={MemberDashboard}/>
<Route path="/override-list" component={OverrideDashboard}/>
</Fragment>
</Router>
)
}
react native wont call render() method as long as there is no state or props update, you need to call this.forceUpdate() to force a rerender. Documentation: https://facebook.github.io/react/docs/component-api.html like
handleLogout = () => {
localStorage.clear("user");
this.forceUpdate()
}
Maybe use the state to save the user when you set to local storage and same thing when you handleLogout?
if(res.data.role === "admin")
{
this.setState({ user: res.data.user_id})
localStorage.setItem("user", res.data.user_id)
this.props.history.push('/member-list');
}
handleLogout = () => {
localStorage.clear("user");
this.setState({ user: ""})
}
So when state is updated, the component re-renders.

Add propTypes to function

I would like add propTypes to my function in React for passing data.
I use createContainer (for Meteor Data) and i would like passing my Data for test if user is logged and if is admin for render my component.
My AdminLayout (using in my React Router) :
const AdminLayout = ({component: Component, ...rest}) => {
console.log(AdminContainer)
if (AdminLayout === true) {
return (
<Route {...rest} render={matchProps => (
<div className="app-container">
<HeaderAdmin />
<main className="l-main">
<Component {...matchProps} />
</main>
<FooterAdmin />
</div>
)} />
)
} else {
return (
<Redirect push to="/connexion"/>
)
}
};
AdminLayout.propTypes = {
isLogged: React.PropTypes.bool,
isAdmin: React.PropTypes.bool
}
AdminContainer = createContainer(() => {
const isLogged = Meteor.userId();
const isAdmin = Meteor.call('is-admin', Meteor.userId(), function(err, data) {
if (err) {
return console.log(err);
} else {
return data;
}
});
return {
isLogged,
isAdmin
};
}, AdminLayout);
export default AdminLayout;
My console.log() return juste function ReactMeteorData() :/ I don't know how i can passing my data in my function.
Anyone can help me ?
Thank you community !

Resources