I have the following PrivateRoute.js code:
export const PrivateRoute = ({ component: Component, roles, ...rest }) => {
const { user } = useContext(AuthContext);
// currentUser = {something: value, something: value, role: "admin" }
const { currentUser } = user;
return (
<Route {...rest} render={props => {
if (!currentUser) {
return <Redirect to={{ pathname: '/login', state: { from: props.location } }} />
}
if (roles && roles.indexOf(currentUser.role) === -1) {
return <Redirect to={{ pathname: '/'}} />
}
// authorised so return component
return <Component {...props} />
}} />
)
}
And this is my app.js file for routing:
<div className="col-md-12">
<PrivateRoute exact path="/" component={HomePage} />
<PrivateRoute path="/page1" roles={[Role.User, Role.Admin]} component={Page1} />
<PrivateRoute path="/page2" roles={[Role.User, Role.Admin]} component={Page2} />
<PrivateRoute path="/page3" roles={[Role.Admin]} component={Page3} />
</div>
UPDATE: AuthContext.js
const parseUser = (x) => ({
currentUser: x,
isAdmin: x && x.role === Role.Admin,
username: localStorage.getItem('email')
});
export const AuthContext = React.createContext({});
export const AuthContextProvider = ({ children }) => {
const history = useHistory();
const [user, setUser] = useState({
currentUser: null,
isAdmin: false
});
const authContext = {
user,
setUserFromStorage: () => {
const userStorage = JSON.parse(localStorage.getItem('loggedUser'));
setUser(parseUser(userStorage));
}
},
login: (email, password) => {
return fetch('calltoAPI')
.fetch(response => {
setUser(parseUser(response));
localStorage.setItem('loggedUser', response);
return response;
})
}
return (
<AuthContext.Provider value={authContext}>
{children}
</AuthContext.Provider>
);
}
This works fine if I'm navigating from the app (with <NavLink to="path" href="path", etc.) the problem is that when I'm trying to access page1, 2 or 3 typing the path directly on the browser url the app redirects me to "/". I write a console.log into PrivateRoute before the if (roles) statement and when I type the url manually is empty.
Why I'm getting this behaviour or what can I do to solve it?
CodePudding user response:
From what I can tell you don't initialize the user
state from the local storage when initializing the AuthContext
, especially after a page reload/refresh. Create a state initialization function to read from localStorage and provide the initial state value.
const defaultState = {
currentUser: null,
isAdmin: false
};
const parseUser = (x) => ({
currentUser: x,
isAdmin: x?.role === Role.Admin,
username: localStorage.getItem('email')
});
/**
* Lazy state initialization
* Read from localStorage and return parsed stored user
* or default initial state object
*/
const initializeFromStorage = () => {
return parseUser(JSON.parse(localStorage.getItem('loggedUser')) || defaultState);
};
export const AuthContextProvider = ({ children }) => {
const history = useHistory();
// Lazy initialize state
const [user, setUser] = useState(initializeFromStorage());
const authContext = {
user,
setUserFromStorage: () => {
const userStorage = JSON.parse(localStorage.getItem('loggedUser'));
setUser(parseUser(userStorage));
},
login: (email, password) => {
return fetch('calltoAPI')
.then(response => {
setUser(parseUser(response));
localStorage.setItem('loggedUser', response);
return response;
});
};
},
};
return (
<AuthContext.Provider value={authContext}>
{children}
</AuthContext.Provider>
);
}
CodePudding user response:
The behavior you are having may be explained by the fact that when you enter directly a url and hit enter, there will be a refresh, so every state
will comes back to its initial value. So if you are handling some authentication, you may wanna use for example localStorage
to save data, and use it when there is refresh, along with states
logic.