【问题标题】:Keeping Material UI tabs and React Router in sync使 Material UI 选项卡和 React Router 保持同步
【发布时间】: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


【解决方案1】:

首先,感谢您回答您的问题。 我以不同的方式处理了这个问题,我决定在这里发帖以供社区欣赏。

我的理由是:“如果我可以告诉 Tab 而不是 Tabs 组件哪个是活动的,那会更简单。”

实现这一点非常简单,可以通过为 Tabs 组件设置一个已知的固定值并将该值分配给任何应该处于活动状态的选项卡。

此解决方案要求托管选项卡的组件可以访问来自 react-router 的位置和匹配等道具,如下所示


首先,我们创建一个工厂函数,从渲染方法中删除臃肿的代码。如果所需的路线匹配,这里将固定的 Tabs 值设置为 Tab,否则我只是抛出一个任意常量,例如 Infinity。

const mountTabValueFactory = (location, tabId) =&gt; (route) =&gt; !!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>
   )
}

【讨论】:

    【解决方案2】:

    您可以使用反应路由器 NavLink 组件

    import { NavLink } from 'react-router-dom';
    
    <NavLink
      activeClassName="active"
      to="/foo"
    >Tab 1</NavLink>
    

    当 /foo 是路由时,active 类将被添加到此链接。 NavLink 也有一个 isActive 属性,可以传递一个函数来进一步自定义确定链接是否处于活动状态的功能。

    https://reacttraining.com/react-router/web/api/NavLink

    【讨论】:

    猜你喜欢
    • 2021-05-19
    • 2020-10-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-25
    • 2016-09-20
    • 2018-12-17
    • 2018-12-19
    相关资源
    最近更新 更多