Code style - MecenatOrg/frontend GitHub Wiki

Tools and architecture

Folder structure

Duck pattern

https://www.freecodecamp.org/news/scaling-your-redux-app-with-ducks-6115955638be/

β”œβ”€β”€shared/
 β”œβ”€β”€ components/
   β”œβ”€β”€ MySharedFeature
    β”œβ”€β”€ index.ts
    β”œβ”€β”€ component.tsx
    β”œβ”€β”€ tests.ts
    β”œβ”€β”€ types.ts
    β”œβ”€β”€ styles.ts
  β”œβ”€β”€ utils.ts
β”œβ”€β”€ pages/
 β”œβ”€β”€ HomePage
  β”œβ”€β”€ index.ts
  β”œβ”€β”€ utils.ts
  β”œβ”€β”€ actions.ts
  β”œβ”€β”€ reducers.ts
  β”œβ”€β”€ selectors.ts
  β”œβ”€β”€ types.ts
  β”œβ”€β”€ components/
   β”œβ”€β”€ MyFeature1
    β”œβ”€β”€ index.ts
    β”œβ”€β”€ component.tsx
    β”œβ”€β”€ tests.ts
    β”œβ”€β”€ types.ts
    β”œβ”€β”€ styles.ts
   β”œβ”€β”€ MyFeature2
    β”œβ”€β”€ index.ts
    β”œβ”€β”€ component.tsx
    β”œβ”€β”€ tests.ts
    β”œβ”€β”€ types.ts
    β”œβ”€β”€ styles.ts

State management (actions.ts, reducers.ts file)

Redux + redux-thunk

https://medium.com/backticks-tildes/setting-up-a-redux-project-with-create-react-app-e363ab2329b8

https://medium.com/@peatiscoding/typescripts-with-redux-redux-thunk-recipe-fcce4ffca405

https://github.com/reduxjs/redux-thunk

CSS in js (styles.ts file)

styled-components

const Button = styled.a`
  /* This renders the buttons above... Edit me! */
  display: inline-block;
  border-radius: 3px;
  padding: 0.5rem 0;
  margin: 0.5rem 1rem;
  width: 11rem;
  background: transparent;
  color: white;
  border: 2px solid white;

  /* The GitHub button is a primary button
   * edit this to target it specifically! */
  ${props => props.primary && css`
    background: white;
    color: palevioletred;
  `}
`

CSS framework

Bootstrap

https://react-bootstrap.github.io/components/alerts

Components to use: Grid section https://react-bootstrap.github.io/layout/grid/

<Container>
  <Row>
    <Col>1 of 1</Col>
  </Row>
</Container>

Selector library (selectors.ts file)

reselect https://github.com/reduxjs/reselect

import { createSelector } from 'reselect'

const shopItemsSelector = state => state.shop.items
const taxPercentSelector = state => state.shop.taxPercent

const subtotalSelector = createSelector(
  shopItemsSelector,
  items => items.reduce((acc, item) => acc + item.value, 0)
)

const taxSelector = createSelector(
  subtotalSelector,
  taxPercentSelector,
  (subtotal, taxPercent) => subtotal * (taxPercent / 100)
)

export const totalSelector = createSelector(
  subtotalSelector,
  taxSelector,
  (subtotal, tax) => ({ total: subtotal + tax })
)

let exampleState = {
  shop: {
    taxPercent: 8,
    items: [
      { name: 'apple', value: 1.20 },
      { name: 'orange', value: 0.95 },
    ]
  }
}

console.log(subtotalSelector(exampleState)) // 2.15
console.log(taxSelector(exampleState))      // 0.172
console.log(totalSelector(exampleState))    // { total: 2.322 }

Typescript

We are using Typescript

https://levelup.gitconnected.com/usetypescript-a-complete-guide-to-react-hooks-and-typescript-db1858d1fb9c

Casing

We use PascalCase for React Component names and TypeScript entity names such as of type, interface, class, enum etc.

We use camelCase for local variable names, object props, function arguments, class members, function names, global scope constants, JSX attributes etc.

Sometimes we use SCREAMING_SNAKE_CASE for constants designating reusable special values being hard-coded with string or number literals.

Neither Hungarian notation nor snake_case is not applicable to our code-base.

Keep index files for re-exports only

Having actual logic inside of index file makes the meaning of it highly dependent on a folder in which it's located and also makes it harder to find through fuzzy search.

// Good
export * as MyComponent from './MyComponent'
 
// Bad
export const MyComponent = () => <span>MyComponent</span>

Selectors naming

Name selectors with "selectSomething" and selector creator functions with "createSomethingSelector", e.g.:

// Good
export const selectCountry = createSelector(...)
export const createCarRouteListSelector = (name: string) => createSelector(...)
 
// Bad
export const getCountry = createSelector(...)
export const getCarRouteList = (name: string) => createSelector(...)
export const selectCarRouteList = (name: string) => createSelector(...)

Order of import statements

In general, imports should use the resource absolute path. Relative paths should be used when importing resources within the component's scope. The order of imports should be: ● Global libraries ● Everything starting with app/or server/, not being app/features/ ● Everything starting with app/features/ ● All relative imports, these should be grouped accordingly. Please keep imports within each group in the alphabetical order. Example:

// global libraries
import { Button } from '@carnext/ui';
import React from 'react';
 
// application resources
import { Util } from 'server/request/util';
 
// occasions resources
import { SomeEnum } from 'app/features/';
 
// local resources
import { MyInterface, Props } from './types';

Events and their handlers

Please prefix event names with onXxx and their respective handlers with handleXxx, not vice versa. For example:

// Good
class Page extends React.PureComponent {
    handleClick = () => {
        alert('Clicked!');
    }
 
    render() {
        return <AwesomeButton onClick={this.handleClick} />;
    }
}

Empty line spacing and code groups

Empty line spacing is more of a matter of taste, but please please try to make it meaningful, use it to group code of the same semantics.

// Good
const { placeholder } = this.props;
const { value } = this.state;
 
return <div>{value || placeholder}</div>;
 
// Bad
const { value } = this.state;
 
const { placeholder } = this.props;
return <div>{value || placeholder}</div>;
 
// Good
const AwesomeButton = styled.button`
    margin: 8px 16px;
    padding: 4px;
 
    background-color: azure;
    color: white;
`;
 
// Bad
const AwesomeButton = styled.button`
    color: white;
    padding: 4px;
    margin: 8px 16px;
    background-color: azure;
`;

Use of typings

Please use TypeScript to your advantage, but please do not clutter the code with unnecessary typings. TypeScript is capable of inferring types in most (if not all) of the cases, just make sure the original data source is typed properly.

interface PrerequisiteState {
    value: string
}
 
interface AppState {
    prerequisite: PrerequisiteState
}
 
const selectPrerequisite = (state: AppState) => state.prerequisite; // return value is properly typed automatically
 
// Good
const selectValue = createSelector(
    selectPrerequisite,
    prerequisite => prerequisite.value // "prerequisite" is properly typed automatically
);
 
const value = selectValue(state); // "value" is typed as "string" automatically
 
// Bad
const selectValue = createSelector(
    selectPrerequisite,
    (prerequisite: PrerequisiteState) => prerequisite.value // typing here is excessive
);
 
const value: string = selectValue(state); // typing here is excessive

React specifics

Avoid using deprecated lifecycle methods

The lifecycle methods below will be deprecated completely in React 17. Please avoid using them in your code. ● componentWillMount ● componentWillReceiveProps ● componentWillUpdate You can find more information about the React updates by the following links: https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html https://reactjs.org/docs/react-component.html

Avoid using constructor for class Components

If in constructor you forget to call super with props passed down, bad things are gonna happen. Also, inline initialization is shorter and easier to read. Example:

// Good
class Counter extends Component<Props, State> {
    state = {
        value: this.props.defaultValue
    };
}
 
// Bad
class Counter extends Component<Props, State> {
    constructor(props: Props) {
        super(props);
        this.state = {
            value: props.defaultValue
        };
    }
}

Functional components vs class components

We strive to use functional components over their class analogues whenever it's possible. Use React.memo(...) once you want an analogue of React.PureComponent. Functional components are considered more light-weight for the React runtime, also they look cleaner in code. Example:

// Good
export const AwesomeButton = ({ text, onClick }): Props => (
    <button onClick={onClick}>{text}</button>
);
 
// Bad
export class AwesomeButton extends React.Component {
    render() {
        const { text, onClick } = this.props;
        return (
            <button onClick={onClick}>{text}</button>
        );
    }
}

Component prop ordering

Please order component props alphabetically, but please try to keep them in groups like these: ● value props ● events ● data- or aria- props Example:

// Good
interface Props {
    color: Color;
    value: string;
    onClick(): void;
    ['data-e2e-id']: string;
}
 
// Bad
interface Props {
    ['data-e2e-id']: string;
 
    value: string;
    onClick(): void;
    color: Color;
}
⚠️ **GitHub.com Fallback** ⚠️