## 第十八步:Stats组件 我们最后一个组件非常简单,仅仅是一个包含一般统计的表格,比如角色总数,按种族、性别、总投票等等统计出来的数据。这些代码甚至都无需解释,因为它们很简单。 ### Component 在app/components新建文件*Stats.js*: ~~~ import React from 'react'; import StatsStore from '../stores/StatsStore' import StatsActions from '../actions/StatsActions'; class Stats extends React.Component { constructor(props) { super(props); this.state = StatsStore.getState(); this.onChange = this.onChange.bind(this); } componentDidMount() { StatsStore.listen(this.onChange); StatsActions.getStats(); } componentWillUnmount() { StatsStore.unlisten(this.onChange); } onChange(state) { this.setState(state); } render() { return ( <div className='container'> <div className='panel panel-default'> <table className='table table-striped'> <thead> <tr> <th colSpan='2'>Stats</th> </tr> </thead> <tbody> <tr> <td>Leading race in Top 100</td> <td>{this.state.leadingRace.race} with {this.state.leadingRace.count} characters</td> </tr> <tr> <td>Leading bloodline in Top 100</td> <td>{this.state.leadingBloodline.bloodline} with {this.state.leadingBloodline.count} characters </td> </tr> <tr> <td>Amarr Characters</td> <td>{this.state.amarrCount}</td> </tr> <tr> <td>Caldari Characters</td> <td>{this.state.caldariCount}</td> </tr> <tr> <td>Gallente Characters</td> <td>{this.state.gallenteCount}</td> </tr> <tr> <td>Minmatar Characters</td> <td>{this.state.minmatarCount}</td> </tr> <tr> <td>Total votes cast</td> <td>{this.state.totalVotes}</td> </tr> <tr> <td>Female characters</td> <td>{this.state.femaleCount}</td> </tr> <tr> <td>Male characters</td> <td>{this.state.maleCount}</td> </tr> <tr> <td>Total number of characters</td> <td>{this.state.totalCount}</td> </tr> </tbody> </table> </div> </div> ); } } export default Stats; ~~~ ### Actions 在app/actions目录新建*Stats.js*: ~~~ import alt from '../alt'; class StatsActions { constructor() { this.generateActions( 'getStatsSuccess', 'getStatsFail' ); } getStats() { $.ajax({ url: '/api/stats' }) .done((data) => { this.actions.getStatsSuccess(data); }) .fail((jqXhr) => { this.actions.getStatsFail(jqXhr); }); } } export default alt.createActions(StatsActions); ~~~ ### Store 在app/store目录新建*Stats.js*: ~~~ import {assign} from 'underscore'; import alt from '../alt'; import StatsActions from '../actions/StatsActions'; class StatsStore { constructor() { this.bindActions(StatsActions); this.leadingRace = { race: 'Unknown', count: 0 }; this.leadingBloodline = { bloodline: 'Unknown', count: 0 }; this.amarrCount = 0; this.caldariCount = 0; this.gallenteCount = 0; this.minmatarCount = 0; this.totalVotes = 0; this.femaleCount = 0; this.maleCount = 0; this.totalCount = 0; } onGetStatsSuccess(data) { assign(this, data); } onGetStatsFail(jqXhr) { toastr.error(jqXhr.responseJSON.message); } } export default alt.createStore(StatsStore); ~~~ 打开routes.js并添加新路由`/stats`。我们必须将它放在`:category`路由之前,这样它会被优先执行。 ~~~ import React from 'react'; import {Route} from 'react-router'; import App from './components/App'; import Home from './components/Home'; import AddCharacter from './components/AddCharacter'; import Character from './components/Character'; import CharacterList from './components/CharacterList'; import Stats from './components/Stats'; export default ( <Route handler={App}> <Route path='/' handler={Home} /> <Route path='/add' handler={AddCharacter} /> <Route path='/characters/:id' handler={Character} /> <Route path='/shame' handler={CharacterList} /> <Route path='/stats' handler={Stats} /> <Route path=':category' handler={CharacterList}> <Route path=':race' handler={CharacterList}> <Route path=':bloodline' handler={CharacterList} /> </Route> </Route> </Route> ); ~~~ 刷新浏览器,你应该看到如下的新Stats组件: ![](https://box.kancloud.cn/2015-09-14_55f6449fd55f8.jpg)