Maximum update depth exceeded from Navigate component react-router-dom v6 - javascript

I'm using react-router-dom v6 to control the route of my React Js app.
Here are the specifications:
I'm creating the AuthenticationRoute and PrivateRoute components.
The AuthenticationRoute component is used to wrap pages that the user doesn't need to authenticate for examples SignIn, SignUp, ForgotPassword, and ResetPassword pages.
The PrivateRoute component is used to wrap private pages (authentication is needed) for example Home page. Inside the PrivateRoute, there are some layouts. One of them is called the Dashboard layout to wrap the Drawer (Sidebar/Navigation) component and the Home page.
If the user has not logged in via the SignIn page, the app would return the SignIn page.
If the user has logged in, the app would return the Home page.
Here are the current conditions:
Note: The check sign (✅) represents the conditions I want while the cross sign (❌) represents the error or unwanted conditions.
All of the specifications above are met. ✅
The first time user runs the app, the SignIn page is returned because the user has not logged in. ✅
If the user has not logged in and typed "/" route to the address bar (to access the Home page) via the SignIn page, the app will not redirect the user to the Home page instead of returning the SignIn page. ✅
If the user successfully logged in via the SignIn page, the app would return the Home page (with "/" route). ✅
If the user has logged in and typed "/sign-in" route to the address bar (to access the SignIn page) via the Home page, the app return error: ❌
Warning: Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn't have a dependency array, or one of the dependencies changes on every render.
at Navigate (https://5xxrw.csb.app/node_modules/react-router/index.js:247:12)
at AuthenticationRoute (https://5xxrw.csb.app/src/components/Routes/AuthenticationRoute.jsx:21:26)
at Routes (https://5xxrw.csb.app/node_modules/react-router/index.js:306:18)
at App
at Router (https://5xxrw.csb.app/node_modules/react-router/index.js:266:18)
at BrowserRouter (https://5xxrw.csb.app/node_modules/react-router-dom/index.js:284:18)
The app should navigate back the user to the Home page ("/" route) instead of returning the error.
Here is the code for the AuthenticationRoute:
function AuthenticationRoute(props) {
const { children } = props;
const userProfile = readUserProfileFromLocalStorage();
return userProfile ? <Navigate replace to="/sign-in" /> : children;
}
and here is the code for the PrivateRoute:
function PrivateRoute(props) {
const { children } = props;
const userProfile = readUserProfileFromLocalStorage();
return userProfile ? (
<Dashboard>{children}</Dashboard>
) : (
<Navigate replace to="/sign-in" />
);
}
Here is the playground: https://codesandbox.io/s/stackoverflow-auth-private-routes-5xxrw
I did a similar thing using react-router-dom v5 but didn't return the error. Everything was fine.
So, what's the solution for this case?

There is an issue on your AuthenticationRoute component. You are redirecting the user to /sign-in when userProfile is defined, which causes an infinite loop since its the same page. It should be navigating to /
function AuthenticationRoute(props) {
const { children } = props;
const userProfile = readUserProfileFromLocalStorage();
return userProfile ? <Navigate replace to="/" /> : children;
}

Related

Add Keycloak authorization in NextJs application (react-keycloak/ssr)

What is the correct way to add keycloak authentication to page component which is server side rendered (using getServerSideProps). I want to ensure that getServerSideProps() method wont be executed if customer is not allowed to view page.
I tried to check if user is authorized inside getServerSideProps() -> it always returns that keycloak.login is not a function.
export const getServerSideProps: GetServerSideProps = async (context:
GetServerSidePropsContext & AppContext) => {
let keycloak = getKeycloakInstance(keycloakConfig,
SSRCookies(parseCookies(context.ctx?.req)))
if (!keycloak?.authenticated) {
keycloak.login();
}
...
Second approach is to add keycloak inside component -> again when I try to call keycloak.login(), same error apears like on first approch (keycloak.login is not a function), but when I test it with console.log(keycloak), then that keycloak object is there with all methods inside it.
import {useKeycloak} from "#react-keycloak/ssr";
import {KeycloakInstance} from "keycloak-js";
...
const ProductDetailPage: React.FC<{
errorCode: number | null;
product: Product; }> = ({ errorCode, product }): ReactElement => {
const { keycloak } = useKeycloak<KeycloakInstance>();
if (!keycloak?.authenticated) {
keycloak.login();
}
...
Conclusion:
When I move keycloak.login() inside useEffect() in second approach, it works and customer is redirected to login but the problem is that page is still shown for a second before redirect. I want to ensure that getServerSideProps() method wont call api and fetch all data for component if customer is not allowed to access that page and that customer is immediatelly redirected to login if tries to access non-public page.

Does React block users from finding components behind login?

I'm new to React and am working on an app that requires user authentication. I have my authentication working fine, basically with redux keeping track of an isSignedIn piece of state, which defaults to false. If false, the user is shown a login form, and if true, the user is shown whatever else they need to see. The login form uses axios to send a post to a server-side authentication script, which responds with a JSON object that includes whether the user is valid or not, and the reducer sets isSignedIn accordingly.
I'm curious though, since the entire app is client-side javascript, can't a nefarious user somehow un-pack the script and modify it locally so they can see the components that normally don't render unless logged in?
Some relevant code snippets from the main App component...
render:
render(){
return (
<div className="ui container">
{
this.props.isSignedIn &&
<Navigation pathname={this.props.pathname} />
}
{
!this.props.isSignedIn &&
<Login />
}
</div>
);
}
mapStateToProps:
const mapStateToProps = (state) => {
return {
isSignedIn: state.isSignedIn
};
};

On redirect from Idp the BrowserRouter doesnt rerender when the previous url from state redirects from the redirect from the idp

I have a react app set up with Auth0
When an anonymous user directly navigate to a protected page
http://localhost:3000/appname/ProtectedPage
the code at the bottom of my component (Auth0 supplied lib)
export default withAuthenticationRequired(ProtectedPage , {
// Show a message while the user waits to be redirected to the login page.
onRedirecting: () => (<div>Redirecting you to the login page...</div>),
});
redirects the user to the Auth0 login page
On successful login the user is redirected back to the authorised redirect url (http://localhost:3000/appname )
The auth0 library passes state back and the original url is loaded into the browser.
The url at this point looks like
http://localhost:3000/appname?code=asdasdadasdadasd
after this the original url is extracted from the state and the app redirects to the desired page
http://localhost/appname/ProtectedPage
This behaviour is what i want so far.
The issue is I have a BrowserRouter which renders when the original response is returned to
http://localhost:3000/appname?code=asdasdadasdadasd
This this renders the HomeComponent
A bit more info
I have two components
The first component contains the BrowserRouter
and has this effect
useEffect(() => {
console.log(`Called useEffect from FI ${window.location.pathname}`);
}, [window.location.pathname]);
The second component (a menu component) is rendered in the BrowserRouter but before the switch
and has this useEffect
useEffect(() => {
const path = window.location.pathname;
console.log(`Called useEffect from FMH ${path}`);
setActiveItem(path);
}, [window.location.pathname]);
I also have used a DebugRouter to try and render some more details from the Browser Router
on redirect from the idp I get the following console outputs
initial history is: {
"length": 26,
"action": "POP",
"location": {
"pathname": "/appname",
"search": "?code=12345&state=iamastate",
"hash": ""
}
}
VM188 main.chunk.js:500 Called useEffect from FMH /appname
VM188 main.chunk.js:173 Called useEffect from FI /appname
VM188 main.chunk.js:500 Called useEffect from FMH /appname/ProtectedPage
Again the ProtectedPage changes but the BrowserRouter doesnt render the correct component
I think the lifecycle seems to be
Redirect received /appname?code
BrowserRouter begings rendering
Url is changed by auth component
Menu component renders and sees the changed url
Browser Router finishes rendering with original url
Possibly the useEffect should react to a change from auth lib but again not sure how to catch it
React-newbie thanks again

React - correct way to wait for page load?

In React JS, what is the correct way to wait for a page to load before firing any code?
Scenario:
A login service authenticates a user then redirects them (with a cookie), to a React App.
The React App then straight away searches for a cookie and validates it against an endpoint.
But the problem I am getting is when user is authenticated at login service, then forwarded to React App, the App is loading so quick before cookie is even loaded.
What is the right way to fire the code? I tried wrapping in a componentDidMount(), didnt work!
I would suggest you to use a state in the Main component of your application (usually App.jsx) which will control loading. When you start the app the state will be true and only after checking all you need to check it will beacome false. If state is loading you will show a spinner or whatever you want and when it is not loading, the website:
class App extends Component {
constructor(props) {
super(props);
this.state = { loading: true }
}
componentDidMount () {
checkToken()
.then(() => this.setState({ loading: false });
}
if (loading) {
return <Spinner /> // or whatever you want to show if the app is loading
}
return (
...rest of you app
)
}
If any doubt just let me know.
you can use Suspense and lazy :)
import React, { Suspense } from 'react';
const OtherComponent = React.lazy(() => import('./OtherComponent'));
function MyComponent() {
return (
<div>
<Suspense fallback={<div>loading...</div>}>
<OtherComponent />
</Suspense>
</div>
);
}

How can I Redirect to the same path?

When I use <Redirect to="<same_path>" /> I get a warning: Warning: You tried to redirect to the same route you're currently on: "<path>".
In my Component I open an other Component thats asking for a submit or cancel.
When the users presses submit an action will be launched and after the action the users has to be redirected to the same Component. So actually, the Component, where the user has to answer the question, will be closed and the Component where Route is at will be reloaded.
I tried this.props.history.push(<path>); as well.
Function what will update the state:
let redirectionLink = "/" + this.props.path;
this.setState({redirectTo: redirectionLink});
In my render:
render() {
if (this.state.redirectTo !== false) {
return <Redirect to={this.state.redirectTo} />
}
}
What I expect it should do is that it just redirects to the gived path. But I get the Warning instead.
If you just want to reload the component, you don't need to redirect. Just change the values of the component that you want to reload and it will automatically render and reload the component.

Categories

Resources