Code style - MecenatOrg/frontend GitHub Wiki
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
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
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;
`}
`
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>
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 }
We are using Typescript
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.
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>
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(...)
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';
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 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;
`;
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
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
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
};
}
}
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>
);
}
}
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;
}