最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - dynamic basename with BrowserRouter in react-router-dom - Stack Overflow

programmeradmin2浏览0评论

Please I have an issue building a multi-tenant SaaS solution. For every tenant, I want them to use a subdomain, so i can get the subdomain from the url, make a call to a REST api that returns data about that tenant.

For example,

  1. the admin (another app entirely - admin app) creates a tenant with domain name: tenant1.
  2. In the tenant application on my local system, I was able to go to tenant1.localhost:3000. I get the url, and get the domain name. I then make a call with the domain to get the theme of tenant (this is stored in localStorage).

Unfortunately, we deploy on k8 in my company and so I couldn't mimic this behavior. So i have been advised by the devOps team to use subdomain in the context, thereby having localhost:3000/tenant1. Remember the tenant is dynamic, so i tried this:

<BrowserRouter basename={"/:tenant"}>
    <Switch>
        <Route exact path="/login" name="Login" component={Login} />
        <Route exact path="/set-password/:token" name="Set Password" component={SetPassword} />
        <PrivateRoute path="/" name="Default Layout" component={DefaultLayout} /> 
    </Switch>              
</BrowserRouter>

The solution above however makes my url to localhost:3000/:tenant/login

Please how can i use dynamic basename in the router, so it can accept:

localhost:3000/tenant1 localhost:3000/tenant3 localhost:3000/tenant2 etc.

It can allow any, my app handles wrong domain inputted

Please I have an issue building a multi-tenant SaaS solution. For every tenant, I want them to use a subdomain, so i can get the subdomain from the url, make a call to a REST api that returns data about that tenant.

For example,

  1. the admin (another app entirely - admin app) creates a tenant with domain name: tenant1.
  2. In the tenant application on my local system, I was able to go to tenant1.localhost:3000. I get the url, and get the domain name. I then make a call with the domain to get the theme of tenant (this is stored in localStorage).

Unfortunately, we deploy on k8 in my company and so I couldn't mimic this behavior. So i have been advised by the devOps team to use subdomain in the context, thereby having localhost:3000/tenant1. Remember the tenant is dynamic, so i tried this:

<BrowserRouter basename={"/:tenant"}>
    <Switch>
        <Route exact path="/login" name="Login" component={Login} />
        <Route exact path="/set-password/:token" name="Set Password" component={SetPassword} />
        <PrivateRoute path="/" name="Default Layout" component={DefaultLayout} /> 
    </Switch>              
</BrowserRouter>

The solution above however makes my url to localhost:3000/:tenant/login

Please how can i use dynamic basename in the router, so it can accept:

localhost:3000/tenant1 localhost:3000/tenant3 localhost:3000/tenant2 etc.

It can allow any, my app handles wrong domain inputted

Share Improve this question edited Mar 13, 2019 at 10:41 Lateefah asked Mar 5, 2019 at 16:50 LateefahLateefah 5751 gold badge5 silver badges14 bronze badges
Add a comment  | 

4 Answers 4

Reset to default 12

I finally used dynamic tenant with the following code

class App extends Component {

    state = {
        domain: ""
    }

    componentWillMount () {
        const { domain } = this.state;

        const parsedData = window.location.pathname.split("/"); 
        let domain = parsedData[1];
        this.setState({ domain: domain })
        this.props.onGetTenant(domain);                
    }

    render () {
        const { domain } = this.state;

        return () {
             <BrowserRouter basename={"/"+domain}>
                <Switch>
                    <Route exact path="/login" name="Login" component={Login} />
                    <Route exact path="/set-password/:token" name="Set Password" component={SetPassword} />
                    <PrivateRoute domain={domain} path="/" name="Default Layout" component={DefaultLayout} /> 
                 </Switch>              
             </BrowserRouter> 
    }

const mapStateToProps = state => {
    const { tenant} = state;
    return { tenant};
};

const mapDispatchToProps = (dispatch) => {
    return {
        onGetTenant: bindActionCreators( tenantActions.get, dispatch)
    }
};

export default connect(mapStateToProps, mapDispatchToProps)(App)

This worked for me using react >16 and react-router-dom v5

export const App = () => {
    return (
        <BrowserRouter>
            <Switch>
                <Route path="/:tenantId?" component={LayoutRoot} />
            </Switch>
        </BrowserRouter>
    );
};

export const LayoutRoot = () => {
    var { tenantId } = useParams();
     //TODO: add some validation here and inform user if tenant is invalid
    return (
        <BrowserRouter basename={tenantId}>
            <Switch>
                <Route path="/login" component={LoginComponent} />
                <Route path="/dashboard" component={DashboardComponent} />
            </Switch>
        </BrowserRouter>
    );
};

You can render updates to your router's basename by using the key property. Any changes to the key value will cause the component to re-render.

Here's a code sandbox to demo: https://codesandbox.io/s/react-router-dom-dynamic-basename-forked-hnkk0?file=/index.js

You can hover or inspect the links in the sandbox to verify that their href values are correctly updating after changing the basename. You can also see that the hrefs won't update if you remove the key property from Router.

import React, { useState } from "react";
import { render } from "react-dom";
import { BrowserRouter, Link } from "react-router-dom";

const Root = () => {
  const [count, setCount] = useState(1);
  const basename = `basename-${count}`;

  return (
    <BrowserRouter basename={basename} key={basename}>
      <Link to="/link1">Link 1</Link>
      <br />
      <Link to="/link2">Link 2</Link>
      <br />
      Current basename: {basename}
      <br />
      <button onClick={() => setCount(count + 1)}>change basename</button>
    </BrowserRouter>
  );
};

render(<Root />, document.getElementById("root"));

Here's a codesandbox and the utility I wrote:

https://codesandbox.io/s/react-router-dom-dynamic-basename-xq9tj?file=/index.js

import urlJoin from 'url-join';

// it's important to have an identifier in their app
export const APP_ROOT_URL = '/my-app';

export const getBaseUrlPath = () => {
  const currentPath = document.location.pathname || APP_ROOT_URL;
  const startOfAppBase = currentPath.indexOf(APP_ROOT_URL);

  let base = currentPath;

  if (startOfAppBase !== -1) {
    base = currentPath.substr(0, startOfAppBase);
  }

  base = urlJoin(base, APP_ROOT_URL);

  return base;
};
发布评论

评论列表(0)

  1. 暂无评论