[英]Keeping Material UI tabs and React Router in sync
是否有一種非駭客的方法來使Material UI標簽和React Router保持同步?
基本上,我想在用戶單擊選項卡[1]時更改URL,並且當用戶使用非選項卡鏈接或按鈕導航到另一個頁面時,當然應該在直接訪問時,選項卡應該自動更改[2]和頁面刷新。
同樣,也可以具有react路由器的非精確功能,因此/foo
和/foo/bar/1
都應同時啟用/foo
選項卡。
[1]其他SO答案建議直接使用歷史記錄api,這是對react-router的良好實踐嗎?
[2]我不確定這叫什么,我的意思是當用戶直接加載例如/foo
而不是加載/
然后通過選項卡或鏈接導航到/foo
編輯:
我創建了一個可以完成此工作的包裝器組件,但是存在一些問題:
class CustomTabs extends React.PureComponent {
constructor() {
super();
this.state = {
activeTab: 0
}
}
setActiveTab(id) {
this.setState({
activeTab: id
});
return null;
}
render() {
return (
<div>
{this.props.children.map((tab,index) => {
return (
<Route
key={index}
path={tab.props.path||"/"}
exact={tab.props.exact||false}
render={() => this.setActiveTab(index)}
/>
);
})}
<Tabs
style={{height: '64px'}}
contentContainerStyle={{height: '100%'}}
tabItemContainerStyle={{height: '100%'}}
value={this.state.activeTab}
>
{this.props.children.map((tab,index) => {
return (
<Tab
key={index}
value={index}
label={tab.props.label||""}
style={{paddingLeft: '10px', paddingRight: '10px', height: '64px'}}
onActive={() => {
this.props.history.push(tab.props.path||"/")
}}
/>
);
})}
</Tabs>
</div>
);
}
}
我正在這樣使用它:
<AppBar title="Title" showMenuIconButton={false}>
<CustomTabs history={this.props.history}>
<Tab label="Home" path="/" exact/>
<Tab label="Foo" path="/foo"/>
<Tab label="Bar" path="/bar"/>
</CustomTabs>
</AppBar>
但:
警告:setState(...):在現有狀態轉換過程中(例如在
render
或另一個組件的構造函數中)無法更新。 渲染方法應該純粹是道具和狀態的函數; 構造函數的副作用是反模式,但是可以將其移至componentWillMount
。
我認為這是因為我在調用render()
之后立即設置了狀態-由於Route.render
,但我不知道如何解決此問題。
編輯#2
我終於解決了所有問題,但是有點笨拙。
class CustomTabsImpl extends PureComponent {
constructor() {
super();
this.state = {
activeTab: 0
}
}
componentWillMount() {
this.state.activeTab = this.pathToTab(); // eslint-disable-line react/no-direct-mutation-state
}
componentWillUpdate() {
setTimeout(() => {
let newTab = this.pathToTab();
this.setState({
activeTab: newTab
});
}, 1);
}
pathToTab() {
let newTab = 0;
this.props.children.forEach((tab,index) => {
let match = matchPath(this.props.location.pathname, {
path: tab.props.path || "/",
exact: tab.props.exact || false
});
if(match) {
newTab = index;
}
});
return newTab;
}
changeHandler(id, event, tab) {
this.props.history.push(tab.props['data-path'] || "/");
this.setState({
activeTab: id
});
}
render() {
return (
<div>
<Tabs
style={{height: '64px'}}
contentContainerStyle={{height: '100%'}}
tabItemContainerStyle={{height: '100%'}}
onChange={(id,event,tab) => this.changeHandler(id,event,tab)}
value={this.state.activeTab}
>
{this.props.children.map((tab,index) => {
return (
<Tab
key={index}
value={index}
label={tab.props.label||""}
data-path={tab.props.path||"/"}
style={{height: '64px', width: '100px'}}
/>
);
})}
</Tabs>
</div>
);
}
}
const CustomTabs = withRouter(CustomTabsImpl);
首先,感謝您回答您的問題。 我對這個問題的處理方式有所不同,因此我決定在此發布帖子,以征詢社區的贊賞。
我在這里的理由是:“如果我能告訴Tab而不是Tabs組件有關哪個是活動的,那會更簡單。”
要做到這一點很簡單,可以通過為Tabs組件設置一個已知的固定值並將該值分配給應該被激活的任何選項卡來實現。
此解決方案要求托管選項卡的組件有權訪問props(例如,位置)和來自react-router的匹配,如下所示
首先,我們創建一個函數,該函數在工廠中從render方法中刪除腫的代碼。 如果所需的路線匹配,則在此處將固定的Tabs值設置為Tab,否則,我只是拋出一個任意常量,例如Infinity。
const mountTabValueFactory = (location, tabId) => (route) => !!matchPath(location.pathname, { path: route, exact: true }) ? tabId : Infinity;
之后,您所需要做的就是將信息插入渲染功能。
render() { const {location, match} = this.props; const tabId = 'myTabId'; const getTabValue = mountTabValueFactory(location, tabId); return ( <Tabs value={tabId}> <Tab value={getTabValue('/route/:id')} label="tab1" onClick={() => history.push(`${match.url}`)}/> <Tab value={getTabValue('/route/:id/sub-route')} label="tab2" onClick={() => history.push(`${match.url}/sub-route`)} /> </Tabs> ) }
您可以使用React Router NavLink組件
import { NavLink } from 'react-router-dom';
<NavLink
activeClassName="active"
to="/foo"
>Tab 1</NavLink>
當/ foo為路由時, active
類將添加到此鏈接。 NavLink
還具有isActive
道具,可以將其傳遞給函數,以進一步自定義確定鏈接是否處於活動狀態的功能。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.