Exercise 4: Solution
Starting file:
////////////////////////////////////////////////////////////////////////////////
// Excercise:
//
// make tabs a "pure component" by not managing any of its own state, instead
// add a property to tell it which tab to show, and then have it communicate
// with its owner to get rerendered with a new active tab.
//
// Why would you move that state up? you might have a workflow where they can't
// progress from one step to the next until they've completed some sort of task
// but they can go back if they'd like. If the tabs keep their own state you
// can't control them with your application logic.
////////////////////////////////////////////////////////////////////////////////
var React = require('react');
var styles = require('./styles');
var data = require('./data');
var Tabs = React.createClass({
propTypes: {
data: React.PropTypes.array.isRequired,
// 9. ++++ add proptype (so other developers immediatly know what to specify)
onActivate: React.PropTypes.func.isRequired
},
// 1. ---- we don't need the State
// ---- getInitialState () {
// ---- return {
// ---- activeTabIndex: 0
// ---- };
// ---- },
handleTabClick (activeTabIndex) {
// 2. ++++ use props
this.props.onActivate(activeTabIndex);
// ---- this.setState({ activeTabIndex });
},
renderTabs () {
return this.props.data.map((tab, index) => {
// 3. ++-- change from using state (this.state.activeTabIndex)
var style = this.props.activeTabIndex === index ? styles.activeTab : styles.tab;
var clickHandler = this.handleTabClick.bind(this, index);
return (
<div key={tab.name} style={style} onClick={clickHandler}>
{tab.name}
</div>
);
});
},
renderPanel () {
// 4. ++-- change this.state.activeTabIndex
var tab = this.props.data[this.props.activeTabIndex];
return (
<div>
<p>{tab.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 App = React.createClass({
// 8. add getInitialState
getInitialState () {
return {
activeTabIndex: 0
}
},
// 7. add handleTab
handleTab (activeTabIndex) {
this.setState({activeTabIndex});
},
render () {
return (
<div>
<h1>Props v. State</h1>
<Tabs
// 5. ++-- add activeTabIndex attribute
activeTabIndex={this.state.activeTabIndex}
data={this.props.tabs}
// 6. ++++ add onActivateTab
onActivateTab = {this.handleTab}
/>
</div>
);
}
});
React.render(<App tabs={data}/>, document.body);
In the case of tabs, why would the parent need to control which one is open?For example the user flow dictates that the user cannot freely jump between the tabs, needs to complete tab1 to move to tab2.
So with the above pattern, the app logic that oversees "everything" can decide wether the user can move to the next tab or not.