在 react-router-dom 中使用 BrowserRouter 的动态基名
dynamic basename with BrowserRouter in react-router-dom
我在构建多租户 SaaS 解决方案时遇到了问题。对于每个租户,我希望他们使用一个子域,这样我就可以从 url 获取子域,调用 REST api 获取有关该租户的 returns 数据。
例如,
- 管理员(完全是另一个应用程序 - 管理应用程序)创建了一个域名为:
tenant1
的租户。
- 在我本地系统上的租户应用程序中,我能够转到
tenant1.localhost:3000
。我得到 url,并得到域名。然后我用域调用以获取租户的主题(这存储在 localStorage 中)。
不幸的是,我们在我公司的 k8 上部署,所以我无法模仿这种行为。因此,devOps 团队建议我在上下文中使用子域,从而得到 localhost:3000/tenant1
。请记住租户是动态的,所以我尝试了这个:
<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>
上面的解决方案使我的 url 变为 localhost:3000/:tenant/login
请问我如何在路由器中使用动态基名,以便它可以接受:
localhost:3000/tenant1
localhost:3000/tenant3
localhost:3000/tenant2
等
它可以允许任何,我的应用程序处理输入的错误域
我终于用下面的代码使用了动态租户
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)
这对我有用 react >16 和 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>
);
};
这是一个codesandbox和我写的实用程序:
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;
};
您可以使用 key
属性 呈现路由器 basename
的更新。对键值的任何更改都会导致组件重新呈现。
这里有一个代码沙箱来演示:
https://codesandbox.io/s/react-router-dom-dynamic-basename-forked-hnkk0?file=/index.js
您可以悬停或检查沙盒中的链接,以验证它们的 href
值在更改基本名称后是否正确更新。如果从 Router
.[=18 中删除 key
属性,您还可以看到 href
s 不会 更新=]
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"));
我在构建多租户 SaaS 解决方案时遇到了问题。对于每个租户,我希望他们使用一个子域,这样我就可以从 url 获取子域,调用 REST api 获取有关该租户的 returns 数据。
例如,
- 管理员(完全是另一个应用程序 - 管理应用程序)创建了一个域名为:
tenant1
的租户。 - 在我本地系统上的租户应用程序中,我能够转到
tenant1.localhost:3000
。我得到 url,并得到域名。然后我用域调用以获取租户的主题(这存储在 localStorage 中)。
不幸的是,我们在我公司的 k8 上部署,所以我无法模仿这种行为。因此,devOps 团队建议我在上下文中使用子域,从而得到 localhost:3000/tenant1
。请记住租户是动态的,所以我尝试了这个:
<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>
上面的解决方案使我的 url 变为 localhost:3000/:tenant/login
请问我如何在路由器中使用动态基名,以便它可以接受:
localhost:3000/tenant1
localhost:3000/tenant3
localhost:3000/tenant2
等
它可以允许任何,我的应用程序处理输入的错误域
我终于用下面的代码使用了动态租户
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)
这对我有用 react >16 和 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>
);
};
这是一个codesandbox和我写的实用程序:
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;
};
您可以使用 key
属性 呈现路由器 basename
的更新。对键值的任何更改都会导致组件重新呈现。
这里有一个代码沙箱来演示: https://codesandbox.io/s/react-router-dom-dynamic-basename-forked-hnkk0?file=/index.js
您可以悬停或检查沙盒中的链接,以验证它们的 href
值在更改基本名称后是否正确更新。如果从 Router
.[=18 中删除 key
属性,您还可以看到 href
s 不会 更新=]
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"));