Skip to content

Instantly share code, notes, and snippets.

@MutableLoss
Created September 18, 2016 06:07
Show Gist options
  • Save MutableLoss/b6ec104c823f105dcc03d68b823e4042 to your computer and use it in GitHub Desktop.
Save MutableLoss/b6ec104c823f105dcc03d68b823e4042 to your computer and use it in GitHub Desktop.
Treehouse React Basics in ES6
// even though we change this data, the array of objects
// will always be exactly that
const players = [
{ key: 1, name: 'D', score: 21 },
{ key: 2, name: 'B', score: 23 },
{ key: 3, name: 'P', score: 13 }
];
class Stopwatch extends React.Component {
constructor() {
super();
this.onStart = this.onStart.bind(this);
this.onStop = this.onStop.bind(this);
this.onReset = this.onReset.bind(this);
this.onTick = this.onTick.bind(this);
this.state = {
running: false,
elapsedTime: 0,
previousTime: 0
}
}
componentDidMount() {
this.interval = setInterval(this.onTick, 100);
}
componentWillUnmount() {
clearInterval(this.interval);
}
onTick() {
if (this.state.running) {
var now = Date.now();
this.setState({
previousTime: now,
elapsedTime: this.state.elapsedTime + (now - this.state.previousTime)
});
}
console.log('tick');
}
onStart() {
this.setState({
running: true,
previousTime: Date.now()
});
}
onStop() {
this.setState({running: false});
}
onReset() {
this.setState({
elapsedTime: 0,
previousTime: Date.now()
});
}
render() {
return (
<div className="stopwatch">
<h2>Stopwatch</h2>
<div className="stopwatch-time">{Math.floor(this.state.elapsedTime / 1000)}</div>
{this.state.running ?
<button onClick={this.onStop}>Stop</button> :
<button onClick={this.onStart}>Start</button>}
<button onClick={this.onReset}>Reset</button>
</div>
)
}
}
// since this is not transpiled code, we need to put all child
// components above the root component. Start at the bottom if
// you are just now opening this file.
class AddPlayer extends React.Component {
constructor() {
super();
this.onNameChange = this.onNameChange.bind(this);
this.onSubmit = this.onSubmit.bind(this);
this.state = {
name: ''
}
}
onSubmit(e) {
e.preventDefault();
this.props.onAdd(this.state.name);
this.setState({name: ''});
}
onNameChange(e) {
this.setState({name: e.target.value});
}
render() {
return (
<div className="add-player-form">
<form onSubmit={this.onSubmit}>
<input type="text" value={this.state.name} onChange={this.onNameChange} />
<input type="submit" value="Add Player" />
</form>
</div>
)
}
}
const Stats = (props) => {
var totalPlayers = props.players.length;
var totalPoints = props.players.reduce((total, player) => {
return total + player.score;
}, 0);
return (
<div>
<table className="stats">
<tbody>
<tr>
<td>Players:</td>
<td>{totalPlayers}</td>
</tr>
<tr>
<td>Total Points:</td>
<td>{totalPoints}</td>
</tr>
</tbody>
</table>
</div>
)
}
Stats.propTypes = {
players: React.PropTypes.array.isRequired
}
// this is a great way to model out a stateless component
// sure we could take in props, but we can be explicit as well
// allowing these to be reused for a specific list of props
// being passed. Think 'functional'. ^_^
// we could also use ...props if rest worked here
const Header = ({players, title}) => {
return (
<div className="header">
<Stats players={players} />
<h1>{title}</h1>
<Stopwatch />
</div>
);
}
Header.propTypes = {
title: React.PropTypes.string.isRequired,
players: React.PropTypes.array.isRequired
}
// showing distinct input of props as exact variables
const Counter = ({score, onChange}) => {
return (
<div className="counter">
<button
className="counter-action decrement" onClick={() => {onChange(-1)}}> - </button>
<div className="counter-score">{score}</div>
<button
className="counter-action increment" onClick={() => {onChange(1)}}> + </button>
</div>
)
}
Counter.propTypes = {
score: React.PropTypes.number.isRequired,
onChange: React.PropTypes.func.isRequired
}
const Player = ({name, score, scoreOnChange, onRemove}) => {
return (
<div className="players">
<div className="player">
<div className="player-name">
<a className="remove-player" onClick={onRemove}>X</a>
{name}
</div>
<div className="player-score">
<Counter score={score} onChange={scoreOnChange} />
</div>
</div>
</div>
);
}
Player.propTypes = {
name: React.PropTypes.string.isRequired,
score: React.PropTypes.number.isRequired,
scoreOnChange: React.PropTypes.func.isRequired,
onRemove: React.PropTypes.func.isRequired
};
class Application extends React.Component {
constructor(state, props){
super(state, props);
// remove the need for a bind step when calling. We could use fat arrow
// declared variable functions that auto-bind, but they are not supported
// in workspaces.
// e.g.: const scoreOnChange = (index, num) => { /*rest of method*/ }
this.scoreOnChange = this.scoreOnChange.bind(this);
this.onPlayerAdd = this.onPlayerAdd.bind(this);
// since ES6 doesn't need or use getInitialState/Props, we use a constructor
this.state = {
title: 'ES6 Scoreboard',
players: this.props.somePlayers,
nextId: players.length + 1
}
}
// look at these beautifully easy to write methods!
scoreOnChange(index, num) {
this.state.players[index].score += num;
this.setState(this.state);
}
onPlayerAdd(name) {
this.state.players.push({
name: name,
score: 0,
key: this.state.nextId
});
this.setState(this.state);
// we don't want nextId's jumping all over the place
// so we make sure the next one IS the next id
this.setState({nextId: this.state.players.length + 1});
}
onRemove(index) {
this.state.players.splice(index, 1);
// we recalc the nextId after removing too
this.setState({nextId: this.state.players.length + 1});
}
render() {
return (
<div className="scoreboard">
<Header title="ES6 Scoreboard" players={this.state.players} />
{this.state.players.map((player, index) => {
return (
<Player
key={player.key}
name={player.name}
score={player.score}
scoreOnChange={this.scoreOnChange.bind(this, index)}
onRemove={this.onRemove.bind(this, index)}
/>
);
})}
<AddPlayer onAdd={this.onPlayerAdd} />
</div>
)
}
}
Application.propTypes = {
title: React.PropTypes.string,
somePlayers: React.PropTypes.arrayOf(React.PropTypes.shape({
name: React.PropTypes.string.isRequired,
score: React.PropTypes.number.isRequired
})).isRequired
}
ReactDOM.render(<Application somePlayers={players} />, document.getElementById('container'));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment