Front-end code specification (TS + React)

Background: In order to unify and standardize the code style of the team as far as possible, and facilitate CR and problem finding, the react + typescript code specification is proposed based on the experience of the past period of time, and the specification will be continuously optimized in the subsequent practice.

Javascript code

Use single or ES6 backquotes, with two Spaces for indentation

Add semicolons at the end of sentences (buchongzidongtinajia)

Does the front-end code only use camelCase? (recommended)

If you convert the front and back end fields, you can add interceptors:

import camelcaseKeys from 'camelcase-keys';
import snakeCaseKeys from 'snakecase-keys';

const handleResponse = (response: AxiosResponse): AxiosResponse= > {
 return camelcaseKeys(response.data, { deep: true }) as unknown as AxiosResponse;
}

export const handleRequest = (request: AxiosRequestConfig): AxiosRequestConfig= > {
 if (request.data) {
   request.data = snakeCaseKeys(request.data , { deep: true });
 }
 if (request.params) {
   request.params = snakeCaseKeys(request.params , { deep: true });
 }
 return request;
}

export const productFeedsBaseURL = '/api';

export const productFeedsRequest: AxiosInstance = Axios.create({
 // baseURL: `http://www.freehard.top${productFeedsBaseURL}`,
 baseURL: '/api'.withCredentials: true
});

productFeedsRequest.interceptors.request.use(handleRequest);

productFeedsRequest.interceptors.response.use(handleResponse, handleErr);
Copy the code

The blank space

Binary and ternary operators must be flanked by a space, and unary operators are not allowed to have a space between them and the operation object.

The opening curly brace {used to start a code block must be preceded by a space.

There must be a space after the if/else/for/while/function/switch/do/try/catch/finally keyword.

When an object is created, : must be followed by a space and: cannot be preceded by a space.

Leave no Spaces inside the parentheses; There is a space before and after the colon and semicolon in parentheses

The code of a single component file should not exceed 400 lines. If so, split the component. No more than 50 lines of single-function code

Code comments

Single-line comment // With one space between the comment content //bad commment // good comment

TODO FIXME: uppercase with a space between the colon and the content. It is recommended to configure VSCode to use TODO Highlight. (recommended)

To avoid spelling errors, it is recommended to use VSCode Code Spell Checker (recommended)

Module import

Import third-party modules (node_modules) first, and then import the current project components. There is a blank line between the third-party module import code segment and the service module import code segment, and a blank line between the import code segment and the subsequent service code segment.

import React, { PureComponent } from 'react'
import { Checkbox, Button, Card, Spin } from 'antd'

import VoiceCollect from './VoiceCollect'
import { AudioPlayerProvider } from '.. /.. /components/AudioPlayer'
import * as API from '.. /.. /api'
import styles from './index.scss'
import { tips } from '.. /.. /common/tools'
import { autoCatch } from '.. /.. /decorators'

const CheckboxGroup = Checkbox.Group
Copy the code

Use Webpack to configure aliases when the import path is too long

Unused modules and variables should be removed

Const > let > var is preferred

Newline operator prefixes (recommended)

The React specification

The basic rule

Only one React component per file Stateless components allow a file to contain multiple components

named

File name: use PascalCase name. eg: ReservationCard.jsx, ReservationCard.tsx

Reference naming: React components are named using PascalCase and instances are named using camelCase

Component name: The component name should be the same as the filename. If a component is in a directory, it should use index.jsx/index.tsx as the filename and the folder name as the component name.

Lower-case naming of other non-components (redux, service)

// bad
import reservationCard from './ReservationCard';

// good
import ReservationCard from './ReservationCard';

// bad
const ReservationItem = <ReservationCard />;

// good
const reservationItem = <ReservationCard />;

// bad
import Footer from './Footer/Footer';

// bad
import Footer from './Footer/index';

// good
import Footer from './Footer';
Copy the code

alignment

Multi-line attributes are indented and can be placed on a single line

// bad
<Foo superLongParam="bar"
     anotherSuperLongParam="baz" />

// good
<Foo
  superLongParam="bar"
  anotherSuperLongParam="baz"
/>

If a component property can be placed on a row, it remains on the current row
<Foo bar="bar" />
Copy the code

quotes

JSX string attributes are quoted in double quotes and others in single quotes

// bad
  <Foo bar='bar' />

// good
  <Foo bar="bar" />

// bad
  <Foo style={{ left: "20px"}} / >// good
  <Foo style={{ left: '20px'}} / >Copy the code

The blank space

Always add a space before the self-closing tag

// bad
<Foo/>

// very bad
<Foo                 />

// bad
<Foo
 />

// good
<Foo />
Copy the code

attribute

Always use camelCase for attribute names

If the property value is true, the assignment of the property is omitted

Properties without Spaces around them

// bad
<Foo
  UserName="hello"
  phone_number={12345678}
  hidden={true}
  loading={ loading }
/>

// good
<Foo
  userName="hello"
  phoneNumber={12345678}
  hidden
  loading={loading}
/>
Copy the code

parentheses

Wrap multi-line JSX tags with parentheses

// bad
render() {
  return <MyComponent className="long body" foo="bar">
           <MyChild />
         </MyComponent>;
}

// good
render() {
  return (
    <MyComponent className="long body" foo="bar">
      <MyChild />
    </MyComponent>
  );
}

// good, when single line
render() {
  const body = <div>hello</div>;
  return <MyComponent>{body}</MyComponent>;
}
Copy the code

The label

Always use a self-closing tag when the tag has no child elements

For multi-line attributes, the closing tag should start on a separate line

// bad
<Foo className="stuff"></Foo>

// good
<Foo className="stuff" />
// bad
<Foo
  bar="bar"
  baz="baz" />

// good
<Foo
  bar="bar"
  baz="baz"
/>
Copy the code

Order in which class component members are arranged

Write in order of normal members, state, lifecycle functions, custom functions, and render functions.

class SameCheck extends PureComponent<Prop.State> {
  algorithms: Algorithm = []
 
  readonly state: State = {
       sampleA: ' '.sampleB: ' '.algorithms: [].submiting: false.result: [].audioBlob: null.formIniting: false
   }

   componentDidMount() {
       this.getInfo()
   }

   handleVoiceAChange = (voiceKey: string) = > {
       this.setState({ sampleA: voiceKey })
   }
   render() {}
}
Copy the code

Use the React. Fragments

// bad
<React.Fragment>
   <div></div>
   <h3></h3>
</React.Fragment>
// good
<>
   <div></div>
   <h3></h3>
</>
Copy the code

Use the arrow function instead of bind

// good
handleClick = (a)= >{}// bad
handleClick() { }
<div onClick={this.handleClick.bind(this)} ></div>
Copy the code

It is not allowed to change state directly, state should be read-only, use setState to change

// bad
this.state.arr.push(xxx)
this.forceUpdate()
// good
this.setState({ arr: this.state.arr.concat([xxx]) })
Copy the code

Avoid memory leaks, clear registered events when component destruction, timer.

Use functional components as much as possible with hooks, reduce use of class components (suggestion)

Use hooks only at the top level; do not call hooks in loops, conditions, or nested functions

The eslint plugin eslint-plugin-react-hooks are recommended

{
  "plugins": [
    // ...
    "react-hooks"]."rules": {
    // ...
    "react-hooks/rules-of-hooks": "error".// Checks rules of Hooks
    "react-hooks/exhaustive-deps": "warn" // Checks effect dependencies}}Copy the code

UseEffect Precautions

Reference:

UseEffect Complete Guide to Using hooks to request data React Hooks dependency Practice guideCopy the code

Avoid getting stuck in a loop

When state is updated in Effect, it causes the component to re-render, which in turn triggers effect, in an infinite loop.

Pass the useEffect second argument [] to trigger effect only when the component is mounted.

// bad 
function App() {
 const [data, setData] = useState({ hits: []}); useEffect(async() = > {const result = await axios(
     'https://hn.algolia.com/api/v1/search?query=redux',); setData(result.data); });return (
   ...
 );
}
// good
function App() {
 const [data, setData] = useState({ hits: []}); useEffect(async() = > {const result = await axios(
     'https://hn.algolia.com/api/v1/search?query=redux',); setData(result.data); } []);return (
   ...
 ); 
}
Copy the code

UseEffect dependencies are listed completely, don’t cheat react

// bad
function Counter() {
  const [count, setCount] = useState(0);

  useEffect((a)= > {
    const id = setInterval((a)= > {
      setCount(count + 1);
    }, 1000);
    return (a)= >clearInterval(id); } []);return <h1>{count}</h1>;
}
// good
function Counter() {
  const [count, setCount] = useState(0);

  useEffect((a)= > {
    const id = setInterval((a)= > {
      setCount(c= > c + 1);
    }, 1000);
    return (a)= > clearInterval(id);

 }, [count]);

  return <h1>{count}</h1>;
}
Copy the code

Typescript specification

TSX is used for ts files with JSX code, ts is used for files without JSX code

Use PascalCase for type names, PascalCase for enumerations, uppercase enumerators, and do not prefix interfaces with I

In every file, the type definition should come first

Minimize the use of any as a type, and make the type as detailed as possible (recommended)

Interface declares sequential read-only parameters first, mandatory parameters second, optional parameters second, uncertain parameters last

interface Props {
  readonly x: number;
  readonly y: number;
  name: string;
  age: number; height? :number;
  [propName: string] :any;
}
Copy the code

Generic (recommended) Record using the TS built-in tool

type Record<K extends keyof any, T> = {
   [P in K]: T;
};
Copy the code

Can be used to declare the type of an object structure.

type Foo = Record<'a' | ‘b’, string>

const foo: Foo = {a: '1'} / / right
const foo: Foo = {b: '1'} // Error because key is not a
const foo: Foo = {a: 1} // Error because value is not a string

Partial
type Partial<T> = {
   [P inkeyof T]? : T[P]; };Copy the code

Changes the required properties of the incoming interface to optional properties.

interface iPeople {
    title: string;
    name: string;
}

const people: Partial<iPeople> = {
    title: 'Delete inactive users'};Copy the code

Required

type Required<T> = {
   [P inkeyof T]-? : T[P]; };Copy the code

Makes an optional attribute of the incoming interface mandatory

interfaceiPeople { title? :string; name? :string;
}

const people: Required<iPeople> = { title: 'ts' }; // Error: property 'name' missing
Copy the code

Readonly

type Readonly<T> = {
   readonly [P in keyof T]: T[P];
};
Copy the code

Makes the attributes of the incoming interface read-only

interface iPeople {
    title: string;
    name: string;
}

const people: Readonly<iPeople> = {
    title: 'todo list';
    name: 'ts';
};
// The title name property is read-only
Copy the code

Pick

type Pick<T, K extends keyof T> = {
   [P in K]: T[P];
};
Copy the code

Extract some attributes from the incoming interface as the new interface.

interface iPeople {
 name: string;
 age: number;
 gender: number;
}

const pickPerson: Pick<iPeople, 'name' | 'age'> = {name: 'test', age: 22};
Copy the code

Exclude

type Exclude<T, U> = T extends U ? never : T;
Copy the code

Rule out T types that can be assigned to U.

type ExcludeType = Exclude<"a" | "b" | "c" | "d"."a" | "c" | "f">;
// ExcludeType = 'b' | 'd'
Copy the code

Extract

type Extract<T, U> = T extends U ? T : never;
Copy the code

Extract the types of T that can be assigned to U.

type ExtractType = Extract<"a" | "b" | "c" | "d"."a" | "c" | "f">;
// ExcludeType = 'a' | 'c'
Copy the code

Omit

type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
Copy the code

Generate a new type based on T, which excludes attributes in K.

interface iPeople {
 name: string;
 age: number;
 gender: number;
}
type OmitPeople = Omit<iPeople, 'name'>;
// OmitPeople = { age: number, gender: number}
Copy the code

Reduced magic number

When writing code, minimize unknown numbers or booleans and use English words.

// bad
if (type! = =0) {
  // TODO
}

// good
const STATUS: Record<string.any> = {
  READY: 0,
  FETCHING: 1,
  FAILED: 2
};

if (type === STATUS.READY) {
  // TODO
}

// best
enum STATUS {
  / / ready
  READY = 0./ / request
  FETCHING = 1.// The request failed
  FAILED = 2,}Copy the code

Use as as type assertion, not <>

interface Foo {
  bar: number;
  bas: string;
}
// bad
const foo = <Foo>{};
foo.bar = 123;
foo.bas = 'hello';
// good
const foo = {} as Foo;
foo.bar = 123;
foo.bas = 'hello';
Copy the code

S