Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should I use react-router for a tabs component?

I'm looking to build a tabs component, where some of the tabs will contain dynamic content. I originally started to follow this tutorial:

Creating a tabs component with React

After reading up on react-router, it seem's like it could also solve this problem. What would be the better approach and/or does it make a difference?

like image 651
mycellius Avatar asked Feb 27 '17 21:02

mycellius


2 Answers

Yes, this is a perfect job for React Router because it focuses on simplifying the URL redirection process for Single Page Apps.

The use case of tabs for navigation, definitely falls under the scope of React Router. You can use React Router 3 or 4 for this, but the React Router 4 API is on the horizon and the documentation is looking great.

You can find a helpful example on the link above that shows how easy it is to link with tabs. And here is an example that discusses how you can create tabs with custom links.

One of the "gotchas" though that you may want to consider, is that there has been some difficulty restoring the scroll position if you navigate to a different route and then navigate back to the previous route. Here is a thread that discusses this issue further: https://github.com/ReactTraining/react-router/issues/1686.

If restoring scroll position is very important to you, then at this point React Router may not be the best fit for tabs.

Update:

React router v4 has been released, the issue above has been resolved, and now the docs have a guide on how to restore scroll position!

like image 168
protoEvangelion Avatar answered Sep 17 '22 14:09

protoEvangelion


I had a lot of issues setting this up (Nov 20, 2017) and thought I'd post the final setup here for posterity. I'm using react-router-dom 4.2.2 and material-ui 0.19.4. Bascially, you want to change the hash (#) when the user clicks tabs, and use the hash to find which tab to display. It works pretty well, but unfortunately it adds a tiny bit of a delay, not sure why and will update if I figure it out.

import React, { Component } from 'react';
import { Tabs, Tab } from 'material-ui/Tabs';

export default class TabsComponent extends Component {
    constructor(props) {
        super(props);
        this.onActive = this.onActive.bind(this);
        this.getDefaultActiveTab = this.getDefaultActiveTab.bind(this);

        this.tabIndices = {
            '#firsttab': 0,
            '#secondtab': 1
        };
    }
    onActive(tab) {
        const tabName = tab.props.label.toLowerCase();
        this.props.history.replace(`#${tabName}`); // https://reacttraining.com/react-router/web/api/history
    }
    getDefaultActiveTab() {
        const hash = this.props.location.hash;
        return this.tabIndices[hash];
    }
    render() {
        return (
            <Tabs
                initialSelectedIndex={this.getDefaultActiveTab()}
            >
                <Tab
                    label="FirstTab"
                    onActive={this.onActive}
                >
                   // ...
                </Tab>
                <Tab
                    label="SecondTab"
                    onActive={this.onActive}
                >
                   // ...
                </Tab>
            </Tabs>
        );
    }
}

And... material-ui ^1.0.0-beta.26

import React, { Component } from 'react';

import Tabs, { Tab } from 'material-ui/Tabs';
import Paper from 'material-ui/Paper';

import { withRouter } from 'react-router-dom';

import Resources from '../resources/index.jsx';
import styled from 'styled-components';

const StyledTabs = styled(Tabs) `
margin:5px;
`;

class LinkableTabs extends Component {
    constructor(props) {
        super(props);
        this.getDefaultActiveTab = this.getDefaultActiveTab.bind(this);
        this.switchTab = this.switchTab.bind(this);

        this.state = {
            activeTabIndex: this.getDefaultActiveTab()
        }
    }
    getDefaultActiveTab() {
        const hash = this.props.location.hash;

        let initTabIndex = 0;
        this.props.tabs.forEach((x, i) => {
            const label = x.label;
            if (`#${label.toLowerCase()}` === hash) initTabIndex = i;
        });

        return initTabIndex;
    }

    switchTab(event, activeTabIndex) {
        this.setState({ activeTabIndex });

        //make shareable - modify URL
        const tabName = this.props.tabs[activeTabIndex].label.toLowerCase();
        this.props.history.replace(`#${tabName}`);
    }
    render() {
        const { match, location, history, staticContext, ...nonrouterProps } = this.props; //https://github.com/DefinitelyTyped/DefinitelyTyped/issues/13689#issuecomment-296246134
        const isScrollable = this.props.tabs.length > 2;
        return (
            <div>
                <Paper>
                    <StyledTabs
                        fullWidth
                        centered
                        scrollable={isScrollable}
                        onChange={this.switchTab}
                        value={this.state.activeTabIndex}
                        {...nonrouterProps}
                    >
                        {
                            this.props.tabs.map(x => <Tab key={x.label} label={x.label} />)
                        }
                    </StyledTabs>
                </Paper>
                {
                    this.props.tabs[this.state.activeTabIndex].component
                }
            </div>
        );
    }
}

export default withRouter(LinkableTabs);
like image 39
James L. Avatar answered Sep 16 '22 14:09

James L.