question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Usage in apps spanning multiple origins/domains

See original GitHub issue

I’m trying to integrate redux-first-router into a site which has multiple domains, and there are some sticking points in my mind relating to these domains. I’m curious if you can see a better solution to what I’ve got, or if you think it would be worth extending the API to support this use case.

The situation

  • There is one main domain at website.com
  • There are many custom domains at client1.com, client2.com, etc
  • Some pages can appear on both domains, e.g. client1.com/settings and website.com/settings both work
  • Some other pages can appear at different paths on the main domain vs the client domain if it exists, e.g. client1.com/pages/somepage is the same type of page as website.com/client2/pages/somepage (client1 has a custom domain but client2 does not).
  • I realise this setup is not optimal, but changing it is impractical.

Intended outcome

  • When we write components that have links or can do navigation, we dispatch routing actions or use redux-first-router-link.
  • We reuse the same components everywhere without regard to which domain they will be rendered on, and the links still work.
  • When a user clicks on a link for a routing action which is available on the domain they are currently on, an SPA transition occurs.
  • When a user clicks on a link for a routing action which is not available on the domain they are currently on, it acts like a link and takes them to the correct page on the other domain, reloading the page on the way (because same origin policy)

Currenty imagined solution

const routesMap = {
  MARKETING: {
    origins: ['https://website.com'],
    path: '/marketing',
  },
  USER_SETTINGS: {
    path: '/settings',
    origins: ['https://website.com', 'https://(?<clientDomain>.*)'],
    stickyOrigin: true,  // Stay on the current domain if possible, even if it isn't first in the list
  },
  CLIENT_PAGE: {
    origins: {
      'https://(?<clientDomain>.*)': {
        path: '/pages/:pageName',
        isAllowed: (getState, action) => !!getState().clients[action.payload.clientPrefix].domain,
      },
      'https://website.com': {
        path: '/:clientPrefix/pages/:pageName',
        isAllowed: (state, action) => !state.clients[action.payload.clientPrefix].domain,
      },
    },
  },
};
dispatch({ type: MARKETING, })

would always go to website.com/marketing, doing an SPA transition if the current origin was already website.com.

dispatch({ type: USER_SETTINGS, })

would always transition to /settings on the current origin

dispatch({
  type: CLIENT_PAGE,
  payload: {
    clientPrefix: 'client1',
    clientDomain: 'https://client1.com',
    pageName: 'apples',
  },
})

would SPA transition from anywhere on client1.com to client1.com/pages/apples, and cause a reload from any other domain

dispatch({
  type: CLIENT_PAGE,
  payload: {
    clientPrefix: 'client2',
    clientDomain: null,
    pageName: 'oranges',
  },
})

would SPA transition from anywhere on website.com to website.com/client2/pages/oranges, and cause a reload from any other domain

The rest seems difficult to implement without adding to the API of redux-first-router. I’m imagining something like this:

  • When matching actions and urls, the usual rules are followed unless a origins property is set on the route. If so, the following is done in order:
    1. If stickyOrigin is set on the route, use the first origin which the current origin matches. Over time, the app can migrate to using this option and not switching domains on internal links.
    2. Use the first origin who’s regex matches the current browser origin and where isAllowed is not set or true or isAllowed(state, action) returns true
    3. Otherwise return undefined or crash.
  • Since actionToPath and pathToAction don’t include an origin, we need actionToUrl and urlToAction. These would be similar except that they would return/receive full urls.
  • actionToPath would at least have to check that the chosen origin for an action is the same as the current option, perhaps return undefined otherwise. pathToAction would need to take into account the current origin when matching the path on routesMap
  • When an action is dispatched that would result in changing the origin, the page should be reloaded at the new origin
  • redux-first-router-link would work similarly, especially the <a /> tag’s href would include the domain when necessary.

github_iconTop GitHub Comments

hedgepigdanielcommented, Jan 31, 2018

In case anyone else is interested in doing something like this, I tried it and it works.

The Wrapped <Link /> component looks something like this:

import { default as RFRLink } from 'redux-first-router-link';
import { actionToPath } from 'redux-first-router';

import {
  getOriginForAction,
  makeRoutesMapForOrigin,
} from '../../ReduxStorage/routesHelpers.jsx';
import { multiDomainRoutesMap } from '../../ReduxStorage/routesMap.jsx';


export default class Link extends Component {
  static propTypes = {
    // Props that only work for RFR/SPA links
    to: PropTypes.object, // Only ations are allowed!
    redirect: PropTypes.bool,
    getState: PropTypes.func.isRequired,
    down: PropTypes.bool,
    shouldDispatch: PropTypes.bool,

    // Props that work with both SPA links and the <a /> tag
    children: PropTypes.oneOfType([
      PropTypes.arrayOf(React.PropTypes.node),
      PropTypes.node,
      PropTypes.string,
    ]),
    onPress: PropTypes.func,
    onClick: PropTypes.func,
    target: PropTypes.string,
    className: PropTypes.string,
  };
  render() {
    const {
      to,
      redirect,
      getState,
      down,
      shouldDispatch,
      ...otherProps
    } = this.props;
    const destinationOrigin = getOriginForAction(
      multiDomainRoutesMap,
      CURRENT_ORIGIN,
      to,
      getState
    );
    if (destinationOrigin !== CURRENT_ORIGIN) {
      // Woe is us, this has to be a regular anchor tag that reloads the page
      // because the destination is on a different domain
      const destinationRoutesMap = makeRoutesMapForOrigin(
        multiDomainRoutesMap,
        destinationOrigin
      );
      const destinationPath = actionToPath(
        to,
        destinationRoutesMap,
        querySerializer
      );
      return (
        <a href={`${destinationOrigin}${destinationPath}`} {...otherProps} />
      );
    } else {
      return (
        <RFRLink {...{ to, redirect, down, shouldDispatch }} {...otherProps} />
      );
    }
  }
}

Passing in getState to the component is probably not ideal, at some point I’ll try and switch it to a thunk that can be dispatched, but the idea is there.

1reaction
faceyspaceycommented, Jan 24, 2018

that works. in the next version of Rudy, the action will have the destination path, so you won’t have to do actionToPath. But yea, onBeforeChange is an even better place to do this.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Progressive Web Apps in multi-origin sites - web.dev
This article will describe the good and bad uses of multiple origins, and explain the challenges and workarounds for building Progressive Web ...
Read more >
Building Progressive Web Apps on multi-origin sites - Medium
Many websites are built on top of multiple subdomains. Reasons for adopting this strategy vary, depending on the vertical, and company, ...
Read more >
Multi-Origin Trusted Web Activities - Chrome Developers
How to create one application using Trusted Web Activities that supports opening multiple origins in full-screen.
Read more >
Cross-Origin Web Sessions
What if you are running multiple applications under different domains and you want to transfer your session state to another domain, ...
Read more >
[GA4] Set up cross-domain measurement - Analytics Help
As a result, a single user visiting different root domains (e.g. www.example.com and www.anotherexample.com) on the same device will be identified separately ( ......
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found