Leveraging the compound component pattern

Our tab headings can only be strings at the moment. What if we want to allow the consumer of the component to define richer content in the headings? For example, a consumer might want to put an icon in front of a tab heading or make a heading bold. So, the consuming JSX could look something like this: 

<Tabs>
<Tabs.Tab name="Description" initialActive={true}>
<b>Description</b>
</Tabs.Tab>
<Tabs.Tab name="Reviews">
Reviews
</Tabs.Tab>
</Tabs>

In the previous example, Tabs and Tabs.Tab are compound components:

  • Tabs is the component that renders the Tabs.Tab components within it. It also manages the state for whichever tab is active.
  • Tabs.Tab renders a single heading. It takes a unique tab name as a property, which allows the active tab to be managed. It also takes in a boolean property called initialActive that sets that tab to be active when first loaded. The heading that is rendered is the content within the Tabs.Tab tag. So, the first tab will render Description in bold. 

So, let's refactor our basic tabs component into a compound component that can be used similarly to the previous example:

  1. Our Tabs component no longer takes in any properties, so, let's remove the IProps interface. We can remove the constructor because we no longer need to initialize the state from the props. Let's also change the name of our state property from activeHeading to activeName as well:
interface IState {
activeName: string;
}
class Tabs extends React.Component<{}, IState> {
public render() {
...
}
...
}
  1. We are going to work on the Tab component within Tabs, first. So, let's create an interface for its properties:
interface ITabProps {
name: string;
initialActive?: boolean;
}
  • The name property is a unique name for the tab. This will be used later to help us manage the active tab.
  • The initialActive property specifies whether the tab is active when the component first loads.
  1. Let's add the following Tab function component inside our Tabs class component now:
class Tabs extends React.Component<IProps, IState> {

public static Tab: React.SFC<ITabProps> = props => <li>TODO - render the nodes child nodes</li>;

public render() {...}

...
}

This is the start of the component that will render each tab. The Tab component is defined as a static property on the Tabs component. This means Tab lives on the actual Tabs class and not in its instances. So, we must remember we don't have access to any Tabs instance members (for instance, this). However, we can reference Tab in JSX using Tabs.Tab now, which was one of our requirements. 

At the moment, Tab is just rendering li with a note reminding us that we need to somehow render the child nodes of the component. Remember that we want the consuming markup for our Tabs component to be as follows:

<Tabs.Tab name="Description" initialActive={true}>
<b>Description</b>
/Tabs.Tab>
  1. So, our render function needs to somehow render <b> Description </b> inside our li tag. How do we do this? The answer is via a special property called children:
public static Tab: React.SFC<ITabProps> = props => <li>{props.children}</li>;
React component properties can be of any type, including React nodes. The children property is a special property that React gives a component that contains the component's child nodes. We render a component's child nodes in JSX by referencing the children property in curly brackets.

Our Tab component is not finished, but we'll leave it like this for the time being. We now need to move on to the Tabs component.

  1. The render method in the Tabs class is simply going to render its child nodes now. Let's replace this method with the following:
public render() {
return (
<ul className="tabs">{this.props.children}</ul>
);
}

We again use the magical children property to render the child nodes within Tabs.

We are progressing well with our compound Tabs and Tab components but our project no longer compiles because we have the tab click handler, handleTabClick, that is not referenced anymore. We need to somehow reference it from the Tab component when a tab heading is clicked, but remember Tab doesn't have access to members of Tabs. So, how can we do this? We'll find the answer to this problem in the next section.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.226.177.125