12

I’m trying to make a tabs component. TabsSwitcher and TabsPanel must be separate components so they could be used anywhere in DOM, e.g. TabsSwitcher doesn’t have to be followed by TabsPanel.

To make it work, I need to somehow connect these components. Furthermore, TabsSwitcher must be able to tell TabsPanel when a tab was clicked.

/** @jsx React.DOM */

var TabsExample = React.createClass({
    render: function() {
        var tabs = [
            {title: 'first', content: 'Content 1'},
            {title: 'second', content: 'Content 2'}
        ];
        return <div>
            <TabsSwitcher items={tabs}/>
            <TabsContent items={tabs}/>
        </div>;
    }
});

var TabsSwitcher = React.createClass({
    render: function() {
        var items = this.props.items.map(function(item) {
            return <a onClick={this.onClick}>{item.title}</a>;
        }.bind(this));
        return <div>{items}</div>;
    },
    onClick: function(e) {
        // notify TabsContent about the click
    }
});

var TabsContent = React.createClass({
    render: function() {
        var items = this.props.items.map(function(item) {
            return <div>{item.content}</div>;
        });
        return <div>{items}</div>;
    }
});

React.renderComponent(
    <TabsExample/>,
    document.body
);

What’s the best way to do it?


Solution: http://jsfiddle.net/NV/5YRG9/

NVI
  • 14,907
  • 16
  • 65
  • 104

1 Answers1

24

The React docs cover this in detail in "Communicate Between Components" and "Multiple Components". The gist is that the parent should pass a function as a prop to the child, and the child should call that function as a callback when it needs to:

var TabsExample = React.createClass({
    handleTabClick: function(item) {
        // Do something with item, maybe set it as active.
    },
    render: function() {
        var tabs = [
            {title: 'first', content: 'Content 1'},
            {title: 'second', content: 'Content 2'}
        ];
        return <div>
            <TabsSwitcher items={tabs} onTabClick={this.handleTabClick}/>
            <TabsContent items={tabs}/>
        </div>;
    }
});

var TabsSwitcher = React.createClass({
    render: function() {
        var items = this.props.items.map(function(item) {
            return <a onClick={this.onClick.bind(this, item)}>{item.title}</a>;
        }.bind(this));
        return <div>{items}</div>;
    },
    onClick: function(item) {
        this.props.onTabClick(item);
    }
});

For the TabsContent component, you should move the tabs into the TabsExample state so React can automatically re-render for you when they change. Since TabsSwitcher and TabsContent are passed the tabs in the render method, React knows they are dependent on the tabs and will re-render when the state changes:

var TabsExample = React.createClass({
    getInitialState: function() {
        return {
            activeTabId: 1,
            tabs: [
                {title: 'first', content: 'Content 1', id: 1},
                {title: 'second', content: 'Content 2', id: 2}
            ]
        };
    };
    handleTabClick: function(item) {
        // Call `setState` so React knows about the updated tab item.
        this.setState({activeTabId: item.id});
    },
    render: function() {
        return (
            <div>
                <TabsSwitcher items={this.state.tabs}
                              activeItemId={this.state.activeTabId}
                              onTabClick={this.handleTabClick}/>
                <TabsContent items={this.state.tabs}
                             activeItemId={this.state.activeTabId}/>
            </div>
        );
    }
});
Ross Allen
  • 43,772
  • 14
  • 97
  • 95
  • "// Do something with item, maybe set it as active.". How can I set "active" class on the corresponding TabsContent div and remove the previously active class? – NVI Jan 02 '14 at 02:32
  • "How can I set "active" class on the corresponding TabsContent div and remove the previously active class?" is actually an odd phrasing for React; that's the jQuery-modify-the-DOM mindset. For React the question is, "How do I set the "active" class based on the tab state?". I will add an example to my code. – Ross Allen Jan 02 '14 at 07:18
  • Check out the bottom code example in the answer now. The `TabsExample` component keeps track of the active tab ID. – Ross Allen Jan 02 '14 at 07:23
  • 1
    Yes, it’s very similar to the solution I came up with: http://jsfiddle.net/NV/5YRG9/. I added it to the question but I think you didn’t notice. – NVI Jan 02 '14 at 07:43
  • Just a question @ssorallen: Why aren't you using the variable name "tabs" ubiquitously in your code snippet? You're using two nouns "tabs" and "items" for one thing. – wle8300.com Oct 16 '15 at 19:53
  • Hi @NVI, your fiddle doesn't work (jsfiddle.net/NV/5YRG9) I get this error : You are using the in-browser JSX transformer. Be sure to precompile your JSX for production – Oliver Watkins Mar 30 '17 at 16:24