React-router-dom in Jira Cloud plugin

Hi!
I stuck with routing in my atlassian connect app.
I have SPA with pages:
<plugin.balse.url>/items - all items are displayed in table with links to every single item
<plugin.balse.url>/items/(id-of-item) - page with single item

when I run this app locally everything is working, but when I deploy my app to my cloud instance, my base url path is changed(because my app is displayed in iframe) to
https://(jira-site).atlassian.net/plugins/servlet/ac/my-plugin/items

All items in the table are shown correct, but when I clicked on the separate item, I recieve 404 error

I think the problem with displaying my app in iframe and (in this case) react-router wouldn’t work here.
I’m not sure about that, so If anyone uses routing in cloud app, please help me

We have tried with Hash tag approach in our app and it worked for us.

https://{domain}.atlassian.net/plugins/servlet/ac/{key}/app-entry?project.key=DEMO&project.id=10000#!/Manage/{newpage}

constructor(props) {
    super(props);
    if (window.AP) {
      window.AP.getLocation(location => {
        
        // Below condition is mandatory for reload application on same page
        if (location) {
          const locParts = location.split("#!");
          if (locParts.length > 1) {
            this.props.history.push(locParts[1]);
          }
        }
      });
      this.props.history.listen(location => {
        let loc = location.pathname;
        if (window.AP) {
          if (location.search) {
            loc += location.search;
          }
          const jiraState = window.AP.history.getState();
          if (loc !== jiraState) {
            window.AP.history.pushState(loc);
          }
        }
      });
      // This will listen instance hash change event and open the page accordingly
      window.AP.history.popState(e => {
        if (e && e.newURL) {
          this.props.history.push(e.newURL);
        }
      });
    }
  }

Thank you! You helped me a lot!

I appreciate the response but could you be more elaborate here? I assume this is a class constructor that extends some component but which component did you extend?

It’s not so complicated actually. Simply use HashRouter as default router from the router-dom package:

import {
HashRouter as Router,
Route,
Switch,
} from ‘react-router-dom’;

You can use react-router normally then

Does that preserve your location after a page reload? For instance, if I navigate to /some/path in my iframe and then reload the site, will I end up on the same page in my iframe?

for that to work, you need to sync the hash changes with AP.history as outlined in the solution by @umang.savaliya

Nice, will have a look at that. Thanks.

I have tweaked @umang.savaliya’s answer to work with React Router v6 and Typescript. The following is all I needed to persist the location across page reloads.

import React, {PropsWithChildren, useEffect} from 'react';
import {useNavigate, useLocation} from 'react-router-dom';

declare global {
    interface Window {
        AP: any;
    }
}

interface RouterSync {
}

function RouterSync({children}: PropsWithChildren<RouterSync>) {
    const navigate = useNavigate();
    const location = useLocation();

    useEffect(() => {
        if (window.AP) {
            window.AP.getLocation((location : any) => {
                if (location) {
                    const locParts = location.split("#!");
                    if (locParts.length > 1) {
                        navigate(locParts[1]);
                    }
                }
            });
        }
    }, []);

    useEffect(() => {
        let loc = location.pathname;
        if (window.AP) {
            if (location.search) {
                loc += location.search;
            }
            const jiraState = window.AP.history.getState();
            if (loc !== jiraState) {
                window.AP.history.pushState(loc);
            }
        }
    }, [location])

    return <>{children}</>;
}

export default RouterSync;

You would then use it as some descendant of HashRouter like so:

root.render(
    <AppContainer>
            <HashRouter>
                <RouterSync>
                    <PageLayout>
                      ...