2

I'm using react dom route v6.

So, I did a structure like this:

    <Route element={<AuthenticationRoute/>}>
        <Route path="/login/" element={<LoginPage />} />
        <Route path="/login/:theme" element={<LoginPage />} />
    <Route>

LoginPage has a form, but before, the app needs to check if the user has some storages, if it was already redirected and then it should render the LoginPage.

We needed to do a lot of redirects and all the condition was stored in the LoginPage and it was causing some troubles while it was trying to load the page. So I needed to change and move the conditions/functions.

Now, the component AuthenticationRoute is a wrapper to redirect if some conditions meet like: doesParameterHasState? getStorageContainUserId?

And at a some point, I need to redirect to another website and it returns back to the same path (login/...) with a hash like this: #state=redirect_uri=value&param=value.

This hash contains all the query string and I need them to render the LoginPage.

There's a function here to change #state=redirect_uri=value&param=value to ?redirect_uri=value&param=value But I need to redirect to login/{theme}/ with search: redirect_uri=value&param=value

I tried to do



export const AuthenticationRoute: FunctionComponent = () => {

const state = changeStateHashToQueryString() // it returns the searchParam (string) or false
const auth = getAuthParam()

    if(auth){
      return redirectToAnotherPage()
    } 

    if (state){
        return (
        <Navigate
          to={{
            pathname: `/login/${getTheme()}`,
            search: state
          }}
        />) 
    }
    
    return loading ? <Loading /> : <Outlet> 
}

// it's working, #state=query_string=489829384 to ?query_string=489829384
export const changeStateHashToQueryString = () => { return window.location.hash ? `?${window.location.hash.substring(7)}` : false }

// its working
export const getAuthParam = () => {
 const params = (new URL(window.location)).searchParams;
 return params.get('auth')
}

export const redirectToAnotherPage = () => {
 // it calls another function that builds the url and redirect to another webpage (another app), it works normally
}

But it gives me a max depth error. I know I should return LoginPage, but I need the query string to be working. If I do pathName "/" it renders correctly, but I need the /:theme/ path to apply the theme. 1 - Should I do window.location.assign or something? 2 - If I need to redirect with frequency like (userHasStorage, userDidAcceptTheTerms, etc), am I doing correctly? Should I make another page or route?

TLDR:

  1. I want to enter the page: https://url.com/login/purpleTheme/#state=queryparam=1234&redirect_uri=1234
  2. Component AuthenticationRoute should condicionally redirect to login (outlet) with queryparam like this: https://url.com/login/purpleTheme/?queryparam=1234&redirect_uri=1234
Lucy
  • 85
  • 6
  • Is `AuthenticationRoute` supposed to be a React component? Why is `state` declared outside the function? Where is `loading` declared? Are you just trying to implement [protected routes](https://stackoverflow.com/a/66289280/8690857)? From what I can tell you are rendering a navigation action to a route you are already on, so this creates a render loop. `AuthenticationRoute` also isn't rendering any `Outlet` component for the nested routes to render their content into. – Drew Reese Feb 16 '23 at 23:26
  • 1) Yes, AuthenticationRoute is a React Component. `state` is a const receiving a value (it changes #state=queryparams=# to ?queryparams=# and returns an string. I didn't include the loading state, but you can consider the inside of AuthenticationRoute as a . But the problem is that I want it to change the url when it's redirecting to LoginPage. ex: I receive https://url/login/purpleTheme/#state={queryparams} and redirect to /login/purple, but with the location as https://url/login/purpleTheme/?{queryparams} – Lucy Feb 17 '23 at 00:31
  • Sorry, I was trying, and failed, to say you've shared an incomplete [mcve]. `element={AuthenticationRoute}` isn't valid in RRDv6, it should be `element={}` if `AuthenticationRoute` is a React component. You'll probably want `const state = changeStateHashToQueryString()` called *in* the component so it gets the correct value at runtime when the route is hit. – Drew Reese Feb 17 '23 at 00:56
  • So, yea, actually I didn't paste the real code, but the AuthenticationRoute is actually like you said: `element={}`. And yes, I'm calling the function inside of the React Component, called `AuthenticationRoute`. It's a wrapper that does some conditions before entering the . As I asked, I'd like to know how to redirect and render the if I receive `https://url.com/login/purpleTheme/#state=queryparam=1234&redirect_uri=1234`, to `https://url.com/login/purpleTheme/?queryparam=1234&redirect_uri=1234` without using window.location.assign if possible – Lucy Feb 17 '23 at 01:12
  • Can you [edit] the post to share a more accurate and complete [mcve] of the code you are working with and trying to use, including this `changeStateHashToQueryString` function? Can you share a complete URL/path that you want to transform, i.e. *what* you want to navigate/redirect *from* and what you want to navigate/redirect *to*? – Drew Reese Feb 17 '23 at 01:20

1 Answers1

1

It seems the main issue is that the AuthenticationRoute component is unconditionally navigating from a "/login/*" route to a "/login/*" route which reruns the if (state) block and rerenders the Navigate component.

You'll want to gather the information you need and conditionally issue an imperative redirect to the "/login/*" path with the appropriate queryString params added.

export const AuthenticationRoute: FunctionComponent = () => {
  const navigate = useNavigate();
  const { pathname } = useLocation();

  // Run effect when the route pathname changes
  useEffect(() => {
    const search = changeStateHashToQueryString();
    if (search) {
      navigate({
        pathname: `/login/${getTheme()}`,
        search
      });
    }
  }, [navigate, pathname]);

  return loading ? <Loading /> : <Outlet />;
}

Edit max-depth-error-on-navigate-with-2-paths-react-router-v6-and-authentication-page

Drew Reese
  • 165,259
  • 14
  • 153
  • 181
  • I tried to do that, but the component breaks (raises our ErrorBoundary component: `React will try to recreate this component tree from scratch using the error boundary you provided, ErrorBoundary.`). If I access /login/ without #state= it renders normally. (but at least it changes the #state to querystring) – Lucy Feb 17 '23 at 01:52
  • @Lucy It's not clear at all what in your or my code that would trigger an exception to be thrown. Think you could create a ***running*** [codesandbox](https://codesandbox.io/) demo of your code that reproduces the routing issue that we could inspect live? – Drew Reese Feb 17 '23 at 01:54
  • @Lucy You might some other issues elsewhere in your code as the code I've provided in my answer appears to work as I expected. Here's a running [codesandbox](https://codesandbox.io/s/max-depth-error-on-navigate-with-2-paths-react-router-v6-and-authentication-page-4z88pd). I've updated my answer to also include the sandbox demo link. – Drew Reese Feb 17 '23 at 02:12
  • So, I'll try to because there's a lot of code in the and I need to be careful, since it's a company code. But I found out what's causing the problem (but idk why though). In the LoginPage, there's a useQuery hook that does a fetch after getting the parameter... `const redirectUri = getRedirectUriFromParam('redirect_uri')` – Lucy Feb 17 '23 at 02:15
  • `const {data, isDataLoading } = useRetrieveDatas(redirectUri)` and then, the child component receives that data. Somehow the child component is breaking. If I comment the line or mock the data, enter the page normally (without using #state=redirect_uri=3213133 but the &redirect_uri=3213133, it works. Weird. – Lucy Feb 17 '23 at 02:15
  • Oh ok, I forgot to run my api. And yea, your solution worked perfectly. Thank you. – Lucy Feb 17 '23 at 02:23
  • Actually, one more question: What would you do in that case? Would you do a wrapper like I did to redirect conditionally or wrap the parent component or something? – Lucy Feb 17 '23 at 02:24
  • @Lucy Sorry, can you clarify what you are referring to or asking about in your last comment? – Drew Reese Feb 17 '23 at 02:51
  • Sorry for not being clear. "In that case" means, if you need to do a lot of redirects and checks before entering the main page, what would be the best practice? Would you do a wrapper like I did? Do you have any suggestions? – Lucy Feb 17 '23 at 03:03
  • @Lucy I see. Using the layout routes to handle auth redirects and such is pretty common, but there's no one single method that stands alone as *the* best way. – Drew Reese Feb 17 '23 at 03:09