Material-ui: Dynamically adding new <Tab>

Created on 27 Jul 2017  路  5Comments  路  Source: mui-org/material-ui

Problem description

Dynamically adding a new object to an array will result in a new <Tab>.
The arrows don't show, but after scrolling to the left they appear.

Minimal working code that reproduces the issue

render() {
    const tabs = this.grids.map((grid) => <Tab key={grid.gridIndex} />);
        return (
            <div>
                <Tabs scrollable scrolling="on">
                       {tabs}
                </Tabs>
            </div>
        )
}

Versions

  • Material-UI: 1.0.0-alpha.20
  • React: 15.5.3
  • Browser: Version 59.0.3071.115 (Official Build) (64-Bit)
Tabs

Most helpful comment

In reviewing the code base, there are currently 3 "events" which cause an evaluation of the scroll button visibility state:

  • Initial render (within componentDidMount)
  • Scrolling the tabs container
  • Resizing the window

There is no handler for a change in the number of child tabs as that wasn't a use case I had originally anticipated. It's likely a trivial change within componentDidUpdate to see if the child tabs have been mutated and then evaluate to determine if the scroll button visibility state needs to be updated.

I'll see if I can work something up and get a PR in for it.

All 5 comments

Any idea what's wrong?

In reviewing the code base, there are currently 3 "events" which cause an evaluation of the scroll button visibility state:

  • Initial render (within componentDidMount)
  • Scrolling the tabs container
  • Resizing the window

There is no handler for a change in the number of child tabs as that wasn't a use case I had originally anticipated. It's likely a trivial change within componentDidUpdate to see if the child tabs have been mutated and then evaluate to determine if the scroll button visibility state needs to be updated.

I'll see if I can work something up and get a PR in for it.

As I think this through, there are a number of things that one could do to a <Tab> child which might cause the scroll buttons to require a change to visibility state. Effectively, any mutation which might change the scrollWidth of the <Tabs> container could mean that the visibility state needs update. For instance, a change to the text or font size of a tab's label could potentially introduce that. Therefore, it can't simply be a check of the number of tab children in order to have a complete solution.

It's likely a trivial change within componentDidUpdate to see if the child tabs have been mutated

I think it will actually be more straightforward and reduce the risk of missing any use cases if I simply add an unconditional call to updateScrollButtonState to componentDidUpdate. That function has a decent level of conditional logic to reduce any performance impact caused by the extra calls.

Prueba as铆:
const tabs = this.grids.map((grid) => { return ( <Tab key={grid.gridIndex} /> ) });

Hey, I made a small gist which might be helpful to as a starter template: https://gist.github.com/Rahul-RB/273dbb24faf411fa6cc37488e1af2415

The same thing is pasted here for quick reference:

import React, { Component } from "react";
import {
    withStyles,
    AppBar,
    Tabs,
    Tab,
    Grid,
    Button
} from "@material-ui/core";
import Add from "@material-ui/icons/Add";
import Close from "@material-ui/icons/Close";
import cloneDeep from "lodash/cloneDeep";

const styles = theme => ({
    root: {
        flexGrow: 1,
        marginTop:"60px",
        width: "100%",
        backgroundColor: theme.palette.background.paper
    },
    appBar:{
        color:"inherit",
        backgroundColor: theme.palette.background.paper
    }
});

class CustomTabs extends Component {
    constructor(...args){
        super(...args);
        this.state = {
            value: 0,
            tabList : [{
                key:0,
                id:0,
            }]
        };
    }

    addTab = () => {
        this.setState((state,props)=>{
            let tabList = cloneDeep(state.tabList);
            let id = tabList[tabList.length-1].id+1;
            tabList.push({
                key:id,
                id:id,
            });

            return {
                tabList,
            }
        })
    }

    deleteTab = (e) => {
        // prevent MaterialUI from switching tabs
        e.stopPropagation();

        // Cases:
        // Case 1: Single tab.
        // Case 2: Tab on which it's pressed to delete.
        // Case 3: Tab on which it's pressed but it's the first tab
        // Case 4: Rest all cases.
        // Also cleanup data pertaining to tab.

        // Case 1:
        if(this.state.tabList.length === 1){
            return; // If you want all tabs to be deleted, then don't check for this case.
        }

        // Case 2,3,4:

        this.setState((state,props)=>{
            let curValue = parseInt(state.value);
            if(curValue === tabID){
                // Case 3:
                if(tabIDIndex === 0){
                    curValue = state.tabList[tabIDIndex+1].id
                }
                // Case 2:
                else{
                    curValue = state.tabList[tabIDIndex-1].id
                }
            }
            return {
                value:curValue
            }
        },()=>{
            this.setState({
                tabList:tabList
            })
        });
    }

    handleTabChange = (event, value) => {

        this.setState({ value });
    }

    render() {
        const { classes } = this.props;
        const { value } = this.state;
        // console.log(this.state);
        return (
            <AppBar position="static" className={classes.appBar}>
                <Grid
                    container
                    alignItems="center"
                    justify="center"
                >
                    <Grid
                        item
                        xl={11}
                        lg={11}
                        md={11}
                        sm={11}
                        xs={11}
                    >
                        <Tabs
                            value={value}
                            onChange={this.handleTabChange}
                            indicatorColor="primary"
                            textColor="primary"
                            variant="scrollable"
                            scrollButtons="auto"
                        >
                            {
                                this.state.tabList.map((tab)=>(
                                    <Tab
                                        key={tab.key.toString()}
                                        value={tab.id}
                                        label={"Node "+tab.id}
                                        icon={
                                            <Close 
                                                id={tab.id}
                                                onClick={
                                                    this.deleteTab
                                                }
                                            />
                                        }
                                        className="mytab"
                                    />
                                ))
                            }
                        </Tabs>
                    </Grid>
                    <Grid
                        item
                        xl={1}
                        lg={1}
                        md={1}
                        sm={1}
                        xs={1}
                    >
                        <Button
                            variant="outlined"
                            onClick={this.addTab}
                        >
                            <Add/>
                        </Button>
                    </Grid>
                </Grid>
            </AppBar>
        );
    }
}

export default withStyles(styles)(CustomTabs);
Was this page helpful?
0 / 5 - 0 ratings

Related issues

finaiized picture finaiized  路  3Comments

anthony-dandrea picture anthony-dandrea  路  3Comments

ghost picture ghost  路  3Comments

newoga picture newoga  路  3Comments

reflog picture reflog  路  3Comments