Search code examples
reactjsecmascript-6react-component

Pattern for react components that require specific contained components


What's the idiomatic React way to write a component that nests specific child components? I know how to write a component that simply wraps props.children, like this example from React's own docs:

function FancyBorder(props) {
  return (
    <div className={'FancyBorder FancyBorder-' + props.color}>
      {props.children}
    </div>
  );
}

But what if I wanted to create a pair of components that can be used like this:

<TabSet>
  <Tab name="Mammals">
     content for <b>warm blooded</b> creatures here
  </Tab>
  <Tab name="Fish">
     content for <b>cold blooded</b> creatures here
  </Tab>
</TabSet>

Here's my initial implementation of TabSet (using reactstrap), simplified to remove styling, selected-tab management, and other stuff not related to this question.

import React, {Fragment, Component} from 'react';
import { TabContent, TabPane, Nav, NavItem, NavLink } from 'reactstrap';

export default class TabSet extends Component {

  render(props) { 
    return (
      <Fragment>
        <Nav tabs>
          {props.children.map((tab,i) => 
            <NavItem key={i}>
              <NavLink>
                { tab.name }
              </NavLink>
            </NavItem>
          )}
        </Nav>
        <TabContent>
          {props.children.map((tab,i) => 
            <TabPane key={i} tabId={i}>
              { tab.children }
            </TabPane>
          )}
        </TabContent>
      </Fragment>
    );
  }

}

Where I'm stuck is how to implement the Tab component. Logically, it's part of the TabSet component-- it should never stand alone. And its implementation should be painfully simple because it doesn't actually do anything-- it's just a container for a name attribute and child elements.

So, here's a few questions:

  • Should I create a separate JS file for the Tab component, or is it so simple that I should just export it as part of the implementation of the TabSet component? If the latter, how?
  • In classes that use TabSet, will I need two import statements, or is there a way I can import both TabSet and Tab with one import, kinda like import React, {Fragment} from 'react' works ? If the latter, then how would the export statement look in TabSet.js?

Apologies for what's probably an obvious question-- I'm a newbie to both React and ES6.


Solution

  • If the component is only used in context of another component it is logical to put them both in same module and many libraries do that. The way to achieve this is use multiple export statements without default. You are allowed to use one export default and as many export statements as you need. Like this

    export default class TabSet 
    ...
    export class Tab
    

    and to import

    import TabSet, {Tab} from './Tab'
    

    The general syntax being

    import defaultExport, { namedExport1, namedExport2  } from "module"
    

    The syntax might seem a bit confusing here is the reference