diff --git a/frontend/react/style-guide.md b/frontend/react/style-guide.md
index d3dc3dfb..1036a80c 100644
--- a/frontend/react/style-guide.md
+++ b/frontend/react/style-guide.md
@@ -26,7 +26,7 @@
- Only include one React component per file.
- Always use JSX syntax.
- - Do not use `React.createElement` unless you're initializing the app from a file that is not JSX.
+ - Do not use `React.createElement`.
- Always use export default for Components.
- Use `export default` for reducers, actionCreators and services. As a general rule of thumb, use `export default` for all files that have a unique object to export.
@@ -38,49 +38,52 @@ src
└───app
│ │
│ └───components
-│ │ └───baseComponents
-│ │ └───Input
-│ │ └───Text
-│ │ └───Button
-│ │ └───etc
+│ | └───FormInput
+│ | └───SearchBar
+│ | └───Table
+│ | └───etc
+| |
│ └───screens
│ └───MyScreenComponent
-│ └───assets // Screen specific app assets
-│ | components
-│ | constants.js
-│ | i18n.js
-│ | index.js
-│ | layout.js
+│ └───assets # Screen specific app assets
+│ └───context
+| | index.ts
+│ | actions.ts
+│ | reducer.ts
+│ └───components # Screen specific components
+│ | constants.ts
+│ | i18n.ts
+│ | index.tsx
│ | styles.scss
-│ | utils.js
+│ | utils.ts
│
-└───assets // General app assets
+└───assets # General app assets
└───config
- | api.js
- | i18n.js
+ | api.ts
+ | i18n.ts
└───constants
-└───redux
-│ | store.js
-│ └───myReducer
-│ | actions.js
-│ | reducer.js
-│ | selectors.js
-│
-└───propTypes
-│ | Model1.js
-│ │ Model2.js
+└───types # Custom global TypeScript classes
+└───contexts # Global contexts
+│ └───Auth
+│ | index.ts
+| | reducer.ts
+| | actions.ts
│
└───scss
└───services
- | MyService.js
+│ │ serializers.ts
+│ └───Model
+│ | ModelService.ts
+│ | serializers.ts
│
└───utils
-│ index.js
+│ index.tsx
```
## Class vs `React.createClass` vs stateless
- - If you have internal state and/or refs, prefer `class extends Component` over `React.createClass`. eslint: [`react/prefer-es6-class`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/prefer-es6-class.md) [`react/prefer-stateless-function`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/prefer-stateless-function.md)
+ - Avoid `React.createClass`. eslint: [`react/prefer-es6-class`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/prefer-es6-class.md) [`react/prefer-stateless-function`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/prefer-stateless-function.md)
+ - Prefer normal functions (not arrow functions) over classes:
```jsx
// bad
@@ -92,49 +95,47 @@ src
});
// good
- class Listing extends Component {
- // ...
- render() {
- return
{this.state.hello}
;
- }
+ function Listing({ hello }) {
+ return
{hello}
;
}
```
-
- And if you don't have state or refs, only for stateless components, prefer normal functions (not arrow functions) over classes:
+ - Prefer normal function components (not arrow functions) over `class extends Component`. Only exception is usage of `componentDidCatch` and `getDerivedStateFromError` (which have no hooks support)
```jsx
// bad
class Listing extends Component {
+ state = { foo: 1 };
+
render() {
- return
{this.props.hello}
;
+ return
{this.state.hello}
;
}
}
- // bad (relying on function name inference is discouraged)
- const Listing = ({ hello }) => (
-
;
+ }
// good
function Listing({ hello }) {
+ const [foo, setFoo] = useState(1);
return
{hello}
;
}
```
- - Avoid using helper render methods when possible. Functions that return JSX elements should probably be layout components.
+ - Avoid using helper render methods when possible. Functions that return JSX elements should probably be components themselves.
```jsx
// bad
- function TextContainer extends Component {
+ function TextContainer({ text }) {
renderText = text => text;
- render() {
- return (
-
- {this.renderText('aText')}
-
- )
- }
+ return (
+
+ {this.renderText(text)}
+
+ )
}
// good
@@ -159,46 +160,37 @@ src
## Naming
- - **Extensions**: Use `.js` extension for React components.
- - **Filename**: For component filenames and services use PascalCase. E.g., `ReservationCard.js`.
+ - **Extensions**: Use `.ts` extension for React components.
+ - **Filename**: For services use PascalCase. E.g., `ReservationCard.ts` or a folder with the service name and `index.tsx` as filename. For React components, there must be a folder in PascalCase with its name and the component file should be `index.tsx`
- **Reference Naming**: Use PascalCase for React components and camelCase for their associated elements. eslint: [`react/jsx-pascal-case`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-pascal-case.md)
```jsx
// bad
- import reservationCard from './ReservationCard';
+ import myService from './MyService';
+ import myComponent from './MyComponent/index.tsx';
// good
- import ReservationCard from './ReservationCard';
+ import MyService from './MyService'; # webpack infers index.tsx
+ import MyComponent from './MyComponent'; # webpack infers index.tsx
// bad
- const ReservationItem = ;
+ const MyComponent = ;
// good
- const reservationItem = ;
+ const myComponent = ;
```
- **Component Hierarchy**:
- Component files should be inside folders that match the component's name.
- - Use index.js as the filename of a container component. Use `Container` as the suffix of the component's name.
- - Use layout.js as the filename of a layout component.
+ - Use index.tsx as the component filename.
```jsx
- // MyComponent/index.js
- import MyComponent from './layout'
+ // MyComponent/index.tsx
- class MyComponentContainer extends Component {
+ function MyComponent() {
// Do smart stuff
- render() {
- return
- }
- }
-
- // MyComponent/layout.js
- function MyComponent() {
- return (
- // Some JSX
- )
+ return
}
```
@@ -254,7 +246,7 @@ src
```jsx
// bad
- /* routes.js */
+ /* routes.ts */
const userListRoute = '/users';
const itemListRoute = '/items';
@@ -262,7 +254,7 @@ src
import * as Routes from './routes';
// good
- /* routes.js */
+ /* routes.ts */
const Routes = {
userListRoute: '/users',
itemListRoute: '/items'
@@ -389,25 +381,20 @@ src
- Always use object destructuring to explicitly get props variables in the render function of class Components:
```jsx
- import MyComponent from './layout';
+ import Child from './components/Child';
// bad
- class MyComponentContainer extends Component {
- render() {
- return
- }
+ function MyComponent(props) {
+ return
}
// good
- class MyComponentContainer extends Component {
- render() {
- const { foo, bar } = this.props;
- return
- }
+ function MyComponent({ foo, bar }) {
+ return
}
```
- - Always use object destructuring to explicitly get props variables in layout Components:
+ - Always use object destructuring to explicitly get props variables:
```jsx
// bad
@@ -456,27 +443,40 @@ src
/>
```
- - Avoid passing arrow functions in props when possible. Instead, create a reference to the function and pass that reference.
+ - Only use inline arrow functions when the arrow function is simple
+ - If the function is complex, define it as a const beforehand. If you are passing it to a child component. you may use `useCallback` for memoization as well.
> Why? Passing arrow functions as props in render creates a new function each time the component renders, which is less performant.
```jsx
- import MyComponent from './layout';
-
// bad
- class MyComponentContainer extends Component {
- render() {
- return bar + 1} />
- }
+ function MyComponent() {
+ return