2017-06-14 14:58:44 +02:00
# React Style Guide
### Prefer Stateless functional components where possible.
Stateless function components are more concise, and there are plans for react to increase performance of them.
Good:
```
export function KuiButton(props) {
2017-09-08 16:53:23 +02:00
return < button className = "kuiButton" { . . . props } / >
2017-06-14 14:58:44 +02:00
};
```
Bad:
```
export class KuiButton extends React.Component {
render() {
2017-09-08 16:53:23 +02:00
return < button className = "kuiButton" { . . . this . props } / >
2017-06-14 14:58:44 +02:00
}
}
```
### When state is involved, use ES6 style React Classes over ES5.
Good:
```
export class ClickCounter extends React.Component {
state = { clickCount: 0 };
onClick = () => {
this.setState(prevState => ({
clickCount: prevState.clickCount + 1
}));
}
render() {
2017-09-08 16:53:23 +02:00
return < button className = "kuiButton" onClick = {this.onClick} / >
2017-06-14 14:58:44 +02:00
}
}
```
Bad:
```
export const ClickCounter = React.createClass({
getInitialState() {
return {
clickCount: 0
};
},
onClick() {
this.setState(prevState => ({
clickCount: prevState.clickCount + 1
}));
},
render() {
2017-09-08 16:53:23 +02:00
return < button className = "kuiButton" onClick = {this.onClick} / >
2017-06-14 14:58:44 +02:00
}
});
```
### When a state change involves the previous state or props, pass setState a function instead of an object.
https://facebook.github.io/react/docs/react-component.html#setstate
Good:
```
this.setState((prevState, props) => ({
clickCount: prevState.clickCount + props.incrementValue
}));
```
Bad:
```
this.setState({ clickCount: this.state.clickCount + this.props.incrementValue });
```
Because this.props and this.state may be updated asynchronously, you should not rely on their values for calculating the next state.
- https://facebook.github.io/react/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous
This will be even more important when the fibers-based implementation is released:
- https://github.com/acdlite/react-fiber-architecture
- https://www.youtube.com/watch?v=ZCuYPiUIONs
### Prefer reactDirective over react-component
2017-09-12 21:57:35 +02:00
When using ngReact to embed your react components inside angular html, prefer
reactDirective over react-component. You can read more about these two ngReact methods [here ](https://github.com/ngReact/ngReact#features ).
Using `react-component` means adding a bunch of components into angular, while `reactDirective` keeps them isolated,
and is also a more succinct syntax.
2017-06-14 14:58:44 +02:00
Good:
```
< hello-component fname = "person.fname" lname = "person.lname" watch-depth = "reference" > < / hello-component >
```
Bad:
```
< react-component name = "HelloComponent" props = "person" watch-depth = "reference" / >
```
### Prefix ui_framework elements with kui, but not their file names.
Good:
```
button.js:
export function KuiButton(props) {
2017-09-08 16:53:23 +02:00
return < button className = "kuiButton" { . . . props } / >
2017-06-14 14:58:44 +02:00
};
```
Bad:
```
button.js:
export function Button(props) {
2017-09-08 16:53:23 +02:00
return < button className = "kuiButton" { . . . props } / >
2017-06-14 14:58:44 +02:00
};
```
The filenames leave it off because snake casing already increases file name length.
### Action function names and prop function names
Name action functions in the form of a strong verb and passed properties in the form of on< Subject > < Change > . E.g:
```
2017-09-08 16:53:23 +02:00
< sort-button onClick = {action.sort}/ >
< pagerButton onPageNext = {action.turnToNextPage} / >
2017-06-14 14:58:44 +02:00
```
### Avoid creating a function and passing that as a property, in render functions.
Best (relies on [stage 2 proposal ](https://github.com/tc39/proposal-class-public-fields )):
```
export class ClickCounter extends React.Component {
state = { clickCount: 0 };
// This syntax ensures `this` is bound within handleClick
onClick = () => {
this.setState(prevState => { clickCount: prevState.clickCount + 1 });
}
render() {
2017-09-08 16:53:23 +02:00
return < button className = "kuiButton" onClick = {this.onClick} / >
2017-06-14 14:58:44 +02:00
}
}
```
Good:
```
export class ClickCounter extends React.Component {
constructor() {
this.state = { clickCount: 0 };
this.onClick = this.onClick.bind(this);
}
onClick() {
this.setState(prevState => { clickCount: prevState.clickCount + 1 });
}
render() {
2017-09-08 16:53:23 +02:00
return < button className = "kuiButton" onClick = {this.onClick} / >
2017-06-14 14:58:44 +02:00
}
}
```
Bad:
```
export class ClickCounter extends React.Component {
state = { clickCount: 0 };
onClick() {
this.setState(prevState => { clickCount: prevState.clickCount + 1 });
}
render() {
2017-09-08 16:53:23 +02:00
return < button className = "kuiButton" onClick = {() = > this.onClick()} />
2017-06-14 14:58:44 +02:00
}
}
```
2017-09-12 21:57:35 +02:00
Also Bad:
```
render() {
return < button className = "kuiButton" onClick = {this.onClick.bind(this)} / >
}
```
2017-06-14 14:58:44 +02:00
Background: https://facebook.github.io/react/docs/handling-events.html
There is also an eslint rule we should be able to turn on for this.
### Never mutate state directly
Good:
```
this.setState(prevState => { clickCount: prevState.clickCount + 1 });
```
Bad:
```
this.state.clickCount += 1;
```
### Prefer primitives over objects when storing in state.
Good:
```
this.setState({
currentPage: 0,
selectedIds: []
});
```
Discouraged:
```
this.setState({
pager: new Pager(),
selectedIds: new SelectedIds()
});
```
### Favor spread operators
```
render() {
2017-09-08 16:53:23 +02:00
return < button className = "kuiButton" { . . . this . props } / >
2017-06-14 14:58:44 +02:00
}
```
```
export function Button({ className, ...rest }) {
const classNames = classNames('KuiButton', className);
2017-09-08 16:53:23 +02:00
return < button className = {classNames} { . . . rest } / >
2017-06-14 14:58:44 +02:00
};
```
## General Guidelines
### Prefer pure functions when possible
Pure functions are easier to understand. We don't want to have to think about side effects or mutated state. When invoking a pure function, all we have to think about is what goes in and what comes out.