【发布时间】:2017-10-12 07:54:12
【问题描述】:
有没有一种非 hacky 的方式来保持 Material UI 选项卡和 React 路由器同步?
基本上,我想在用户单击选项卡 [1] 时更改 URL,并且当用户使用非选项卡链接或按钮导航到不同页面时,选项卡应自动更改,当然还有直接访问[2] 和页面刷新。
此外,最好也拥有 react 路由器的 非精确 功能,因此 /foo 选项卡应该对 /foo 和 /foo/bar/1 都处于活动状态。
[1] 其他 SO 答案建议直接使用 history 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或其他组件的构造函数中)。渲染方法应该是 props 和 state 的纯函数;构造函数副作用是一种反模式,但可以移至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);
【问题讨论】:
-
你能发布你的代码的小sn-ps来更清楚地理解你的问题吗?
标签: reactjs react-router material-ui