const Star = ({ isActive }) => { const classNames = ['star']; const activeClass = 'star--active'; if (isActive) { classNames.push(activeClass); } return <span className={classNames.join(' ')}>★</span>; }; const RatingWidget = ({ size, rating, onRatingChange, onChangeBegin, onChangeAbort }) => { var starItems = []; for (let i = 0; i < size; i++) { starItems.push( <li key={i} className="star-item" onClick={onRatingChange.bind(null, (i + 1))} onMouseEnter={onChangeBegin.bind(null, (i + 1))} onMouseLeave={onChangeAbort.bind(null)}> <Star isActive={i < rating}/> </li> ); } return ( <ul className="rating-widget">{starItems}</ul> ); }; const RatingWidgetContainer = React.createClass({ getInitialState: function() { return { rating: this.props.rating }; }, handleChangeBegin: function(rating) { this.setState({ rating: rating }); }, handleChangeAbort: function() { this.setState({ rating: this.props.rating }); }, render: function() { return <RatingWidget size={this.props.size} rating={this.state.rating} onRatingChange={this.props.onRatingChange} onChangeBegin={this.handleChangeBegin} onChangeAbort={this.handleChangeAbort}/> } }); // model let appState = { rating: 0 }; // controller const updateRating = (newRating) => { appState.rating = newRating; renderApp(); }; // bootstrap function renderApp() { ReactDOM.render( <RatingWidgetContainer size={5} rating={appState.rating} onRatingChange={updateRating}/>, document.getElementById('root') ); } renderApp();
<div id="root"></div> <script src="https://fb.me/react-0.14.8.js"></script> <script src="https://fb.me/react-dom-0.14.8.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.34/browser.js"></script>
.star { color: #CCC; cursor: pointer; } .star--active { color: #FFDD99; } .star-item { display: inline; } .rating-widget { margin: 0; padding: 0; list-style-type: none; }