【问题标题】:antd tabs to use react-routerantd 标签使用 react-router
【发布时间】:2018-06-06 20:48:53
【问题描述】:

想知道是否有一种方法可以基于每个选项卡的嵌套路由来渲染路由。

例如,当我们浏览到

http://localhost:3000/base/a
http://localhost:3000/base/b

我们希望有一个base 组件,它提供某种布局,并基于我们单击的tab 应该呈现nested route 组件。

以下示例不使用 react-router 来渲染选项卡的组件。

下面是路由配置

<Route path={"/base:scope" } exact={true} { component: BaseComponent } />

下面的组件将呈现带有标签的页面

import { Layout, Tabs, Row, Col } from 'antd';
import { Link, NavLink, Route, Switch } from 'react-router-dom';
export class BaseComponent extends React.Component {

    public state = {
        collapsed: false,
    };

    private onCollapse = collapsed => {
        this.setState({ collapsed });
    }

    get tabs() {
        const { collaborators, integration, settings, configuration, billing, tokens, referrals } = this.props;
        return [
            {
                key: 'a',
                tab: 'a',
                component: ComponentA,
                path: '/base/a',
            },
            {
                key: 'b',
                tab: 'b',
                component: ComponentB,
                path: '/base/b',
            },
        ];
    }

    public componentWillReceiveProps(props) {
        if (!_.isEqual(_.get(this.props, 'match.url'), _.get(props, 'match.url'))) {
            this.setState({ key: _.get(props, 'match.url') });
        }
    }

    public render() {
        const { renderer, router } = this.context;
        const { onLogOut, match, user, profile } = this.props;

        return (
            <React.Fragment>
                <Header className={renderer.renderRule(styles.header, this.props)}>
                    <div className={renderer.renderRule(styles.title, this.props)}>
                        Account Settings
                    </div>
                </Header>
                <Content>
                    <div className={renderer.renderRule(styles.container, this.props)}>
                        <Tabs
                            animated={false}
                            defaultActiveKey={match.url}
                            onChange={key => router.history.push(key)}
                            className={`${renderer.renderRule(styles.tabs)}`}
                        >
                            {_.map(this.tabs, (record, index) => (
                                <TabPane
                                    key={record.path}
                                    className={renderer.renderRule(styles.pane)}
                                    tab={<span>{record.tab}</span>}
                                >
                                    {React.createElement(record.component, null, null)}
                                </TabPane>

                            ))}
                        </Tabs>
                    </div>
                </Content>
            </React.Fragment >
        );
    }
}

预期:

我们想写成更具体的 react-router

<Routes path={"/base"} exact={false} component={ComponentBase} />
<Routes path={"/base/a"} exact={true} component={ComponentBase} />
<Routes path={"/base/b"} exact={true} component={ComponentBase} />

但是在这种情况下,我们不知道如何渲染页面,因为 react-router 不渲染页面,所以我们注意到选项卡但没有渲染内容。

这是修改后的组件,没有渲染内容,因为我们期望 react-router 渲染组件。

export class BaseComponent extends React.Component {

    public state = {
        collapsed: false,
    };

    private onCollapse = collapsed => {
        this.setState({ collapsed });
    }

    get tabs() {
        const { collaborators, integration, settings, configuration, billing, tokens, referrals } = this.props;
        return [
            {
                key: 'a',
                tab: 'a',
                path: '/base/a',
            },
            {
                key: 'b',
                tab: 'b',
                path: '/base/b',
            },
        ];
    }

    public componentWillReceiveProps(props) {
        if (!_.isEqual(_.get(this.props, 'match.url'), _.get(props, 'match.url'))) {
            this.setState({ key: _.get(props, 'match.url') });
        }
    }

    public render() {
        const { renderer, router } = this.context;
        const { onLogOut, match, user, profile } = this.props;

        return (
            <React.Fragment>
                <Header className={renderer.renderRule(styles.header, this.props)}>
                    <div className={renderer.renderRule(styles.title, this.props)}>
                        Account Settings
                    </div>
                </Header>
                <Content>
                    <div className={renderer.renderRule(styles.container, this.props)}>
                        <Tabs
                            animated={false}
                            defaultActiveKey={match.url}
                            onChange={key => router.history.push(key)}
                            className={`${renderer.renderRule(styles.tabs)}`}
                        >
                            {_.map(this.tabs, (record, index) => (
                                <TabPane
                                    key={record.path}
                                    className={renderer.renderRule(styles.pane)}
                                    tab={<span>{record.tab}</span>}
                                >
                                </TabPane>

                            ))}
                        </Tabs>
                    </div>
                </Content>
            </React.Fragment >
        );
    }
}

【问题讨论】:

    标签: reactjs react-router-v4 antd


    【解决方案1】:

    我尝试使用 antd Tabs 创建 RoutedTabs 组件。我已经添加了相同的文档字符串。

    import React from "react";
    import PropTypes from "prop-types";
    import { Tabs } from "antd";
    import { Switch, Route } from "react-router-dom";
    
    import _each from "lodash/each";
    import _map from "lodash/map";
    const { TabPane } = Tabs;
    /**
     * RoutedTabs is Ant Design based dynamic navigation tabs component.
     * It takes router config as prop and render the component as well as update the window location corrosponding to the tab.
     * Example of routeConfig
     * overview: {
            label: "Overview",
            component: RestaurantOverview,
            getRoute: url => url
        },
        menu: {
            label: "Menu",
            component: Menu,
            getRoute: url => `${url}/menu`
        },
        "menu-holiday-slot": {
            label: "Menu Holiday Slot",
            component: MenuHolidaySlots,
            getRoute: url => `${url}/menu-holiday-slot`
        }
        This will render three tabs Overview, Menu, Menu Holiday Slot and routes based on what getRoute method returns.
     */
    const RoutedTabs = props => {
        const { tabsProps, routeConfig } = props;
        const { url, path } = props.match;
        const tabToRouteMap = {};
        const routeToTabsMap = {};
        _each(routeConfig, (configObj, routeKey) => {
            const routeURL = configObj.getRoute(url);
            tabToRouteMap[routeKey] = routeURL;
            routeToTabsMap[routeURL] = routeKey;
        });
        const defaultActiveKey = routeToTabsMap[props.history.location.pathname];
        const tabPaneNodes = _map(routeConfig, (configObj, routeKey) => (
            <TabPane tab={configObj.label} key={routeKey} />
        ));
        const routeNodes = _map(routeConfig, (configObj, routeKey) => (
            <Route
                path={configObj.getRoute(path)}
                exact
                key={routeKey}
                component={configObj.component}
            />
        ));
        const onTabChange = activeKey => {
            props.history.push(tabToRouteMap[activeKey]);
        };
        return (
            <>
                <Tabs {...tabsProps} onChange={onTabChange} defaultActiveKey={defaultActiveKey}>
                    {tabPaneNodes}
                </Tabs>
                <Switch>{routeNodes}</Switch>
            </>
        );
    };
    
    RoutedTabs.propTypes = {
        tabsProps: PropTypes.object.isRequired, // As per https://ant.design/components/tabs/#Tabs
        routeConfig: PropTypes.object.isRequired,
        match: PropTypes.object.isRequired,
        history: PropTypes.object.isRequired
    };
    
    export default RoutedTabs;

    【讨论】:

      【解决方案2】:

      如果您确定要使用路由器显式定义选项卡,那么您可以在组件中设置一个道具:

      <Route path={"/base/a"} exact={true} render={() => <ComponentBase tab="a" />} />
      

      More info on Route from react router docs.

      我认为您最好使用 react 路由器提供的 props.match.params,而不是显式定义选项卡 more info and example here

      您可以从 {props.match.params.base} 中获取值并拉出该选项卡。

      【讨论】:

      • 所以你建议我们应该使用props.match,这就是我们在问题第一部分所说的?
      猜你喜欢
      • 2018-06-21
      • 1970-01-01
      • 2018-05-20
      • 1970-01-01
      • 1970-01-01
      • 2016-06-07
      • 1970-01-01
      • 1970-01-01
      • 2020-04-28
      相关资源
      最近更新 更多