Exercise 3: Solution

In notebook:
FrontEndMasters React
Created at:
2016-06-14
Updated:
2016-06-15
Tags:
libraries React JavaScript
Need to add the click handlers, etc.
How do I get which tab was clicked?
  ////////////////////////////////////////////////////////////////////////////////
// Excercise:
// - make these tabs work when you click them
////////////////////////////////////////////////////////////////////////////////
var React = require('react');
var assign = require('react/lib/Object.assign');

var DATA = [..];

var App = React.createClass({
    
    // 9. ++++ finally we can define the getInitialState method
    // and the this.state.activeTabIndex inside it
    getInitialState () {
        return {
            activeTabIndex: 0
        }  
    },
    
        // 3. ++++ Add the click handler
    handleTabClick (activeTabIndex) {
        // 11. ++-- change to activeTabIndex
        this.setState({activeTabIndex: activeTabIndex });
        // or do it the ES6 way:
        // this.setState({ activeTabIndex });
    },

  renderTabs () {
    //  4. ++++ Actually moves out tabIndex variable
    var ativeTabIndex = 0;
    return this.props.countries.map((country, index) => {
      return (
        //   1. ++++ add the click event
        //   2. ++++ bind the handler to pass the index 
        //  5. ++++ changes style={index === 0 to index === activeTabIndex
        // 10. ++-- changes all this to use the state
        // ----<div onClick={this.handleTabClick.bind(this, index)} style={index === activeTabIndex ? styles.activeTab : styles.tab}>
          {country.name}

        <div
        // rewrite the onClick handler
            onClick={this.handleTabClick.bind(this, index)}
            style={index === activeTabIndex ? styles.activeTab : styles.tab}>
        // 12. ++++ add a key to remove the React warning (so it can track the nodes)
            key={country.name}
            >
          {country.name}

        </div>
      );
    });
  },

  renderPanel () {
    //   6. ++++ use the activeTabIndex here as well
    //  8. ++-- changes activeTabIndex = 0 to activeTabIndex = this.state.activeTabIndex
    var activeTabIndex = this.state.activeTabIndex;
    // 7. ++-- changes countries[0] to countries[activeTabIndex]
    var country = this.props.countries[activeTabIndex];
    return (
      <div>
        <p>{country.description}</p>
      </div>
    );
  },

  render () {
    return (
      <div style={styles.app}>
        <div style={styles.tabs}>
          {this.renderTabs()}
        </div>
        <div style={styles.tabPanels}>
          {this.renderPanel()}
        </div>
      </div>
    );

  }
});

var styles = {};

styles.tab = { .. };

styles.activeTab = assign({}, styles.tab, { .. });

styles.tabPanels = {.. };

React.render(<App countries={DATA}/>, document.body);

To recap:
  • render UI
  • attach handlers (e.g. click)
  • modifies the state
  • automatically re-render in a stateless fashion

Defining CSS style rules in JavaScript:

See bottom of previous snippet, expanded here:
  ..

var styles = {};

styles.tab = {
  display: 'inline-block',
  padding: 10,
  margin: 10,
  borderBottom: '4px solid',
  borderBottomColor: '#ccc',
  cursor: 'pointer'
};

// this is how you add styles
styles.activeTab = assign({}, styles.tab, {
  borderBottomColor: '#000'
});

styles.tabPanels = {
  padding: 10
};

React.render(<App countries={DATA}/>, document.body);
- Isn't it bad practice to call ​bind​ on a function?
Shows a standard JS example where you add ​addEventListener​ to a DOM element:
​el.addEventListener(someHandler.bind(this))​ . You will not be able to remove this with ​removeEventListener​ since ​bind​ just created a new function.
It's because of this side-effect, that this pattern is considered bad practice. 

Under the hood React creates synthetic events. Events get delegated. So when you define an ​onClick​ attribute on a JSX element, it does not add a real ​click​ attribute on the element. It just tell React to catch this click event, when the big, global click event happens. 

Now, in React you don't have to think about states in your app. You can make the ​onClick​ value conditional based on the state and React will remove it:
​<div onClick={shouldAddHandler ? this.handleTabClick.bind(this, index) : null}