A mostly reasonable approach to React and JSX
- Basic Rules
- Method Ordering
renderfunctions- Observers and Handlers
React.createClassvs Pure Components- Default Props
- Naming
- Spacing
- Props
- Alignment
- Tags
- Methods
isMounted
- Only include one React component per file.
- However, multiple Stateless, or Pure, Components are allowed per file. eslint rule:
react/no-multi-comp.
- However, multiple Stateless, or Pure, Components are allowed per file. eslint rule:
- Always use JSX syntax.
All methods within your componenet should be ordered using the following list:
mixinsgetDefaultPropsgetInitialState- Lifecycle Methods, eg.
componentWillMount,componentDidMount - Event Handlers like
handleSubmitClick()orhandleChangeDescription() - Getter methods for
render*functions likegetSelectReason()orgetFooterContent() - Additional render methods like
renderNavigation()orrenderProfilePicture() render
- The logic within your
renderfunction should be kept at a minimum. If there is any looping, or complex conditions, it should be broken out into it's ownrender*method.
// bad
var Foo = React.createClass({
render : function() {
return <ul className="items">
{_.map(this.props.items, (item) => {
return <li key={item.id}>{item.text}</li>
})}
</ul>;
}
});
// good
var Foo = React.createClass({
renderItems : function(){
return _.map(this.props.items, (item) => {
return <li key={item.id}>{item.text}</li>
})
},
render : function() {
return <ul className="items">
{this.renderItems()}
</ul>;
}
});- Any prop passed to a component that is a function meant to be an "observer" should be prefixed by
on*, eg.onChange,onSubmit,onUserSelect
// bad
<Foo click={this.handler} whenUserSubmits={this.handleSubmit} />
// good
<Foo onClick={this.handler} onUserSubmit={this.handleSubmit} />- Any function within a function that reacts to external changes should be prefixed with
handle*, eg.handleKeyPress,handleUserSelect
// bad
var Foo = React.createClass({
onClick : function(){
/* ... */
},
render : function() {
return <button onClick={this.onClick}>Click me!</button>;
}
});
// good
var Foo = React.createClass({
handleClick : function(){
/* ... */
},
render : function() {
return <button onClick={this.handleClick}>Click me!</button>;
}
});- It's common that a
handle*function may just call an observer prop. This is fine, as often additional logic or logging will be added later.
// bad
var Foo = React.createClass({
getDefaultProps : function(){
return {
onClick : function(){}
}
},
render : function() {
return <button onClick={this.props.onClick}>Click me!</button>;
}
});
// good
var Foo = React.createClass({
getDefaultProps : function(){
return {
onClick : function(){}
}
},
handleClick : function(){
this.props.onClick();
},
render : function() {
return <button onClick={this.handleClick}>Click me!</button>;
}
});- Use the
createClasssyntax for creating components
// good
var Listing = React.createClass({
// ...
render : function() {
return <div>{this.state.hello}</div>;
}
}- If your componenet does not use state of refs, use function notation instead.
// bad
var Listing = React.createClass({
render : function() {
return <div>{this.props.hello}</div>;
}
})
// good
var Listing = function({ hello }){
return <div>{hello}</div>;
}- If your component is expecting props, they should be defined within the
getDefaultProps
Why not
propTypes? You can use Prop Validation, but our preferred method is prop defaulting. Validation tends to be more strict and less backwards compatible when working with shared components.
// bad
var Foo = React.createClass({ /* ... */ });
<Foo unexpectedProp="Neat!" />
// good
var Foo = React.createClass({
getDefaultProps : function(){
return {
unexpectedProp : ""
}
}
/* ... */
});- Especially make sure to stub out any handlers with noop functions
// good
var Foo = React.createClass({
getDefaultProps : function(){
return {
onChange : function(){}
}
}
});- Extensions: Use
.jsxextension for React components. - Filename: Use camelCase for filenames. E.g.,
reservationCard.jsx. - Reference Naming: Use PascalCase for React components.
// bad
var reservationCard = require('./ReservationCard.jsx');
// good
var ReservationCard = require('./reservationCard.jsx');- Component Naming: Use the filename as the component name. For example,
reservationCard.jsxshould have a reference name ofReservationCard.
- Always include a single space in your self-closing tag.
// bad
<Foo/>
// very bad
<Foo />
// bad
<Foo
/>
// good
<Foo />- Always use camelCase for prop names.
// bad
<Foo
UserName="hello"
phone_number={12345678}
/>
// good
<Foo
userName="hello"
phoneNumber={12345678}
/> // bad
render() {
return <MyComponent
className="long body" foo="bar">
<MyChild />
</MyComponent>;
}
// good
render() {
return <MyComponent className="long body" foo="bar">
<MyChild />
</MyComponent>
}
// good, when single line
render() {
const body = <div>hello</div>;
return <MyComponent>{body}</MyComponent>;
}- Always self-close tags that have no children.
// bad
<Foo className="stuff"></Foo>
// good
<Foo className="stuff" />- Do not use underscore prefix for internal methods of a React component.
// bad
React.createClass({
_onClickSubmit : function() {
// do stuff
},
// other stuff
});
// good
React.createClass({
onClickSubmit : function() {
// do stuff
}
// other stuff
}- Do not use
isMounted.
Why?
isMountedis an anti-pattern, is not available when using ES6 classes, and is on its way to being officially deprecated.