React-specific guidelines described at RingCentral React Style Guide
This document provides a set of common rules regarding to source code in JavaScript. It allow us to make and keep our codebase well organized and maintainable.
use brackets for all control constructs like if, else, try, for, while. Even for one liners
Reason: using brackets makes code as predictable as possible. It avoid bugs caused to forgotten bracket by misprint. Also it's don't require developer to add/remove brackets when body of construct transform from one line to multi-line form and vice versa. It makes diffs more clear.
// BAD
let bar = [];
if (isValid(foo)) bar.push('bar');
// BAD
if (isValid(foo))
bar.push('bar');
// BAD
for (let i=0; i<AMOUNT; i++) bar.push(i);
// GOOD
if (isValid(foo)) {
bar.push('bar');
}
// GOOD
for (let i=0; i<AMOUNT; i++) {
bar.push(i);
}// BAD
let foo = 'foo'
let bar = ['bar', 'baz']
// GOOD
let foo = 'foo';
let bar = ['bar', 'baz'];Reason: it allows to avoid bugs with global scope variables (caused by misprinted comma). It's easier to maintain. It makes diffs more clear.
// BAD
const FOO = 'FOO',
BAR = 17;
let foo, bar = 17;
// GOOD
const FOO = 'FOO';
const BAR = 17;
let foo;
let bar = 17;// BAD
if (foo == '') {
bar();
}
if (foo == null) {
bar();
}
// GOOD
if (foo === '') {
bar();
}
if (foo === null) {
bar();
}It easy to misprint and leads to bugs
// BAD
if (foo.bar = baz) {
calculate();
}
// GOOD
foo.bar = baz;
if (foo.bar === true) {
calculate();
}It's a bad practice and may cause scope side bugs. See MDN for reference.
For block-scoped definition use const or let.
// BAD
if (foo) {
var result = 'foo';
}
return result;
// GOOD
var result;
if (foo) {
result = true;
}
return result;
// GOOD
if (foo) {
let result = true;
}
// BAD
for (var i=0; i<AMOUNT; i++) {
var item = foo[i];
}
// GOOD
for (let i=0; i<AMOUNT; i++) {
let item = foo[i];
}// BAD
function qux() {
if (foo) {
function bar() {
// ...
}
// ...
bar();
}
}
// GOOD
function qux() {
if (foo) {
// ...
bar();
}
}
function bar() {
// ...
}
// GOOD, but avoid inner functions
function qux() {
if (foo) {
// ...
bar();
}
function bar() {
// ...
}
}Reason:
letis block scoped, vsvaris function scoped. Using unassignedletvariable cause anReferenceErrorexception, but unassignedvarstill work silently without warnings and may cause hard to reproduce bugs. One more reason to useletis that redeclaration the same variable will triggerSyntaxErrorversus silent continuation of execution withvardeclaration
// BAD
function foo() {
var bar = baz();
// ...
var bar = null; // work
}
// GOOD
function foo() {
let bar = baz();
// ...
let bar = baz(); // SyntaxError exception prevent re-declaration
}
// BAD
function foo() {
baz(bar); // work, bar === undefined
// ...
var bar = 'bar';
}
// GOOD
function foo() {
baz(bar); // SyntaxError
// ...
let bar = 'bar';
}// BAD
model.
foo().
bar().
baz;
// GOOD
model
.foo()
.bar()
.baz();
// BAD
expect(actualValue, 'should contains foo, bar').
to.have.ordered.members(['foo', 'bar']);
// GOOD
expect(actualValue, 'should contains foo, bar')
.to.have.ordered.members(['foo', 'bar']);It's really hard to maintain such code
// BAD
let result = foo < bar ? foo : (foo > baz ? 'FOO' : baz);
// GOOD
let result;
if (foo < bar) {
result = foo;
} else {
result = foo > baz ? 'FOO' : baz;
}// BAD
condition && foo();
// GOOD
if (condition === true) {
foo();
}
// BAD
isCondition && foo();
// GOOD
if (isCondition) {
foo();
}Reason: it's improve file navigation
Do not use with statement - it cause of confusing and bad maintainable code
// BAD
with (_) {
trace(sort(bar), trim(baz));
}
// GOOD
trace(_.sort(bar), _.trim(baz));Reason: it bad for testing and maintaining. It's always better to make separate, outer-scope free functions and methods instead of mash of inner functions.
Do not leave console calls in your code
Reason: it just a noise for other developers. We always can find anything using history of version control system.
Reason: It's hard to read and maintain
Exception: long links and international strings can exceed that limitation
Reason: alignment may increase readability for some cases, but it's a root of few problems. It bad for maintaining, because of change of one line can trigger realignment of all nearest aligned lines. So, owner of such change will mislead reviewers, and makes commits history dirty. Also it cause a redundant merge conflicts.
// BAD
let foo = {
first: 'foo',
second: 'bar',
thisOneIsLong: 'baz'
};
// GOOD
let foo = {
first: 'foo',
second: 'bar',
thisOneIsLong: 'baz'
};// BAD
function isFooBar () {
// ...
}
// GOOD
function isFooBar() {
// ...
}// BAD
if (isValid){
// ...
}
// GOOD
if (isValid) {
// ...
}
// BAD
while (foo > bar){
// ...
}
// GOOD
while (foo > bar) {
// ...
}// BAD
if(isValid) {
foo();
}else{
bar();
baz();
}
// GOOD
if (isValid) {
foo();
} else {
bar();
baz();
}
// BAD
if(isValid) {
foo();
}else if(quux < 0){
bar();
baz();
}
// GOOD
if (isValid) {
foo();
} else if (quux < 0) {
bar();
baz();
}
// BAD
for(const foo of listFoo) {
bar(foo);
}
// GOOD
for (const foo of listFoo) {
bar(foo);
}// BAD
if (isFoo(bar)) {
foo();
}
else {
bar();
}
// GOOD
if (isFoo(bar)) {
foo();
} else {
bar();
}
// BAD
if (isFoo(bar)) {
foo();
} else if (quux > baz)
{
bar();
}
else {
bar();
barBaz();
}
// GOOD
if (isFoo(bar)) {
foo();
} else if (quux > baz) {
bar();
} else {
bar();
barBaz();
}Naming should be expressive. It should be maximally clear and avoid unnecessary abbreviations.
ClassName should be same as ClassName.js. Avoid multiple classes per file. ClassName should begin with Letter in upper case.
// GOOD
ThingsStore.js
Logger.jsmethodName should be verbs like and be in camelCase
// GOOD
sumbitMessage(message) { /* */ }
save(data) {/* */ }
hasMessages() { /* */ }variableName should be in camelCase
// GOOD
let store = new Store();
let formatParser = getParser();ENUMERIC_CONSTANTS should be ALL_IN_UPPER_CASE
// GOOD
const MODE_EDIT = 'MODE_EDIT';
const PARSER_ID_MARKDOWN = 14;Boolean variables and methods who returns Boolean values should contains verb prefix (has, is, should, etc.)
// GOOD
const isValid = isEmail(email);
// GOOD
let canProceedFlow = this.getListLength() > 0;
isUserAuthorized() {
if ( /* */ ) {
return true;
}
}To make handlers organized keep following rules:
use handle prefix for class members
use on prefix for component props
use action right away after prefix onClickButton no onButtonClick
use first-verb form of words in names
export class MyAwesomeComponent extends React.PureComponent {
// ...
// GOOD
handleClose = () => {
// event handler logic
};
// GOOD
handleClickNextButton = event => {
// event handler logic
};
// GOOD
handleClickPrevButton = event => {
// event handler logic
};
// ALSO GOOD
handleConnectMediaStreamView = payload => {
// event handler logic
};
// STILL GOOD
handleChangeStreamQuality = nextValue => {
// event handler logic
};
render() {
return (
<Panel closable onClose={this.handleClose}>
<MediaStreamView
stream={this.getStream()}
onConnect={this.handleConnectMediaStreamView}
onChangeStreamQuality={this.handleChangeStreamQuality}
/>
<Button onClick={this.handleClickPrevButton}Prev</Button>
<Button onClick={this.handleClickNextButton}>Next</Button>
</Panel>
);
}
}