
How it works...
Let's start by looking at the markup used to render the tabs in this following example:
<TabContainer>
<TabContent label="Item One">Item One Content</TabContent>
<TabContent label="Item Two">Item Two Content</TabContent>
<TabContent label="Item Three">Item Three Content</TabContent>
</TabContainer>
This markup is much more concise than using the Tab and Tabs components directly. This approach also handles rendering the content of the selected tab. Everything is self-contained with this approach.
Next, let's take a look at the TabContainer component:
function TabContainer({ children }) {
const [value, setValue] = useState(0);
const onChange = (e, value) => {
setValue(value);
};
return (
<Fragment>
<Tabs value={value} onChange={onChange}>
{Children.map(children, child => (
<Tab label={child.props.label} />
))}
</Tabs>
{Children.map(children, (child, index) =>
index === value ? child : null
)}
</Fragment>
);
}
The TabContainer component handles the state of the selected tab and changing the state when a different tab is selected. This component renders a Fragment component so that it can place the selected tab content after the Tabs component. It's using Children.map() to render the individual Tab components. The label of the tab comes from the label property of the child. In this example, there are three children (TabContent). The next call to Children.map() renders the content of the selected tab. This is based on the value state—if the child index matches, it's the active content. Otherwise, it gets mapped to null and nothing is rendered.
Lastly, let's take a look at the TabContent component:
const TabContent = withStyles(styles)(({ classes, children }) => (
<Typography component="div" className={classes.tabContent}>
{children}
</Typography>
));
TabContent takes care of styling the Typography component and renders the child text within. Although the label property is passed to TabContent, it doesn't actually use it; instead, it's used by TabContainer when rendering tabs.