2
\$\begingroup\$

I am an experienced programmer, but a complete beginner with React. My goal is to teach myself React.

I've made a simple die-roller application. The application presents a drop-down, and the user can choose what die to roll (e.g. a d6 is a six-sided die). When the user clicks "roll", an appropriate random number is generated and displayed.

The app is working fine, but I would be grateful to know whether I've written genuinely "React-y" code; whether it's structured properly, whether I have got the naming conventions right, etc.

Some specific questions: is the "Dice" constant declared in the right place/way? Should the "option" elements be full React components? Should the "button" be a full React component?

import React, { Component } from 'react';
import './App.css';
const Dice = ["4", "6", "8", "10", "12", "20"];
class App extends Component {
 constructor(props) {
 super(props);
 this.state = {
 outcome: null,
 sides: null
 };
 this.handleClick = this.handleClick.bind(this);
 this.setSides = this.setSides.bind(this);
 }
 render() {
 return (
 <div className="App">
 <header className="App-header">
 Die roller
 </header>
 <DieChooser dice={Dice} setSides={this.setSides}></DieChooser>
 <button onClick={this.handleClick}>Roll</button>
 <Result outcome={this.state.outcome} />
 </div>
 );
 }
 setSides(s) {
 this.setState({sides: s});
 }
 handleClick() {
 const rolled = Math.floor(Math.random() * (this.state.sides)) + 1;
 this.setState({ outcome: rolled })
 }
}
class DieChooser extends Component {
 constructor(props) {
 super(props);
 this.handleChange = this.handleChange.bind(this);
 this.props.setSides(props.dice[0]);
 }
 handleChange(e) {
 this.props.setSides(e.target.value)
 }
 render() {
 return (
 <select onChange={this.handleChange}>
 {this.props.dice.map((die) =>
 <option key={die} value={die}>d{die}</option>
 )}
 </select>
 )
 }
}
function Result (props) {
 return (
 <p>{props.outcome}</p>
 )
}
export default App;
200_success
145k22 gold badges190 silver badges478 bronze badges
asked Dec 4, 2018 at 10:49
\$\endgroup\$
1
  • 1
    \$\begingroup\$ if you use arrow function like handleClick = () => {}; in the class, you don't need to bind it in the constructor. \$\endgroup\$ Commented Dec 4, 2018 at 18:52

1 Answer 1

2
\$\begingroup\$

With the new ES6 syntax, arrow functions allow you to remove .bind from your code.

Don't make a stateful component if you're not using its state. Your DieChooser doesn't handle its value change by itself since its parent component is handling it. Make a stateless(function) component whenever you can and do not require to use lifecycle methods or a private state.

I also moved the setSides up to the parent to improve readability.

A select tag will always select the first option by default; there is no need for this.props.setSides(props.dice[0]);.

Props deconstructing will also make your code more readable, as the last component Result only takes a single line now.

Working live example :

const Dice = [4, 6, 8, 10, 12, 20];
class App extends React.Component {
	constructor(props) {
		super(props);
		this.state = {
			outcome: null,
			sides: null
		};
	}
	render = () => {
		return (
			<div className="App">
				<header className="App-header">
					Die roller
 </header>
				<DieChooser dice={Dice} setSides={this.setSides} />
				<button onClick={this.handleClick}>Roll</button>
				<Result outcome={this.state.outcome} />
			</div>
		);
	}
	setSides = event => {
		this.setState({ sides: event.target.value });
	}
	handleClick = ev => {
		const rolled = Math.floor(Math.random() * (this.state.sides)) + 1;
		this.setState({ outcome: rolled })
	}
}
const DieChooser = ({ dice, setSides }) => {
	return (
		<select onChange={setSides}>
			{dice.map( die =>
				<option key={die} value={die}>d{die}</option>
			)}
		</select>
	)
}
const Result = ({ outcome }) => <p>{outcome}</p>
ReactDOM.render(<App/>, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.3.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.3.0/umd/react-dom.production.min.js"></script>
<div id='root'/>

Toby Speight
87.1k14 gold badges104 silver badges322 bronze badges
answered Jan 8, 2019 at 8:52
\$\endgroup\$
1
  • \$\begingroup\$ Just few very minor things: setSides() (rightly) moved up in the tree but it still depends on an implementation detail (e.target.value). I'd rather abstract this away (if, for example, I'll change DieChooser to click an image in a grid...then I also have to change code here). App might be a PureComponent (not that it makes any real difference here, I just consider it a - opinionated - good habit for non functional ones). \$\endgroup\$ Commented Jan 9, 2019 at 18:01

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.