React Development (Typescript)

File Structure

We strictly stipulate the composition of the file directory.

CRA Project
|-- .gitignore # Git Ignore
|-- .prettierrc # Prettier syntax configuration
|-- package.json # Dependency declaration file
|-- README.md # README
|-- tsconfig.json # TypeScript configuration
|-- yarn.lock # Dependency Version declaration file
|-- .github # GitHub Configuration
|   |-- ...
|-- .idea # Idea configuration (should all be ignored)
|-- public # Documents released to the public
|   |-- favicon.ico # Must be here
|   |-- ...
|-- src
    |-- App.tsx # Router and global configuration
    |-- index.tsx # Index component, Provider
    |-- react-app-env.d.ts
    |-- reportWebVitals.ts # Web stat configuration
    |-- setupProxy.js # Backend proxy configuration for develop
    |-- setupTests.ts # Tests configuration
    |-- components # Components
    |   |-- Uploader # Component
    |   |   |-- index.tsx # Default component
    |   |   |-- ...
    |   |-- Layout # Layout
    |   |-- Admin # Sub page components
    |   |   |-- index.tsx # Sub page route
    |   |   |-- Layout # Sub page layout
    |   |   |   |-- ...
    |   |   |-- ...
    |   |-- ...
    |-- i18n # Internationalization
    |   |-- index.ts # I18n instance
    |   |-- translation # translations
    |       |-- index.ts # Default
    |       |-- template.ts # Translate template
    |       |-- zh-cn.ts # Translation extends template
    |       |-- en-us.ts # Translation extends template
    |-- middleware # Middleware
    |   |-- Api # Api instance
    |   |   | -- ...
    |   |-- Route # Route checker (Better distinguish whether a user is logged in or not)
    |       |-- index.ts # Default
    |       |-- AuthRoute.tsx # Only logged in users can access (e.g. background pages)
    |       |-- CommonRoute.tsx # Accessible to all users (e.g. home page)
    |       |-- NoAuthRoute.tsx # Can only be accessed by users who are not logged in (e.g. login pages and registration pages)
    |-- model # Data model
    |   |-- base # Base data (e.g. UserData)
    |   |   |-- userData.ts
    |   |   |-- ...
    |   |-- request # Data required when sending Api requests
    |   |   |-- index.ts # Global request data model
    |   |   |-- imageDeletePayload.ts # One request data model, end with "Payload"
    |   |   |-- imageUploadPayload.ts
    |   |   |-- loginPayload.ts
    |   |   |-- ...
    |   |   |-- admin # Api request for management page
    |   |   |   |-- overviewDataPayload.ts
    |   |   |   |-- ...
    |   |-- response # Data returned by Api requests
    |       |-- index.ts # Global response data model
    |       |-- batchImagesResponse.ts # The data returned is usually based on the base data, end with "Response"
    |       |-- loginResponse.ts
    |       |-- uploadResponse.ts
    |       |-- ...
    |       |-- admin # Api response for management page
    |       |   |-- overviewDataResponse.ts
    |       |   |-- ...
    |-- redux # Global state manager, effectively decouple
    |   |-- hooks.ts # Custom hooks
    |   |-- store.ts # Redux store
    |   |-- reducers # Redux reducers
    |       |-- uploader.ts
    |       |-- viewUpdate.ts
    |       |-- ...
    |-- services # Services bases on redux
    |   |-- api.ts # Api instance bases on api middleware and redux-toolkit
    |-- utils # Custom tools
        |-- md5.ts
        |-- ...

Prettier

Manage code style with Prettier.

Install

yarn add --dev prettier # add for development dependency

.prettierrc Example

{
  "tabWidth": 4,
  "printWidth": 100
}

Absolute Paths

Configure in tsconfig.json to use absolute paths.

{
  "compilerOptions": {
    "baseUrl": "./src/",
    "paths": {
      "~/*": ["*"]
    }
  }
}

Named

The naming of all front-end variables, functions, and classes follows the following principles:

  1. Use lower camel case for all the variables.
  2. Use lower camel case for all the file names.
  3. Use lower camel case for functions, such as getUser().
  4. Use lower camel case for interfaces which are not exported.
  5. Use upper camel case for interfaces which are exported.
  6. Use upper camel case for components, such as <Component/>.
  7. Use upper camel case for all the classes.

Also, the names of variables, functions, classes, and interfaces must be associated with their definition and purpose.

For example:

  1. for variables representing site configuration, we use siteConfig, in all cases.
  2. for variables representing usernames, we use username, in all cases.
  3. for functions that represent getting site configuration, we use getSiteConfig.
  4. for the interface representing the type of the return value of the Get Site Config interface, we use SiteConfigResponse.
  5. for the interface representing the request value to query the user interface, we use UserGetPayload.
  6. for the interface representing the parameters required to upload an image, we use ImageUploadPayload.
  7. for an interface representing the type of component parameter, we use ComponentProps.

We also have some hard requirements:

  1. The start of the curly brace must immediately follow the left-hand, without a newline.
  2. The use of the var and function keywords in code is prohibited under any circumstances.
  3. The code must be formatted by Prettier, and individuals are not allowed to modify the Prettier configuration file without permission.
  4. The code must pass the ESLint check of the project configuration, and no one is allowed to change the ESLint configuration without permission.

Components

Storing Redux JSX components.

  1. We want to have only one main component in a file, and the split widgets are placed in different files.
  2. Each component uses a separate sub folder, the default reference is located in index.tsx.
  3. Use lower camel case for subdirectory names of components.
  4. We encourage the use of functional components.
  5. The width of a single line of code must not exceed 120 bytes.
// /src/components/exampleComponent/index.tsx

// Although this is not required to run, we require that we start with import React.
import React from "react"; // must end with a semicolon.
import SubComponent from "./subComponent"; // Subcomponents within the same component use relative paths.
import { useAppDispatch } from "~/redux/hooks"; // Functions outside the component are imported using absolute paths.

// The component parameters define the Props interface separately, must be exported.
// Named as component name + "Props", use upper camel case.
export interface ExampleComponentProps {
    text: string; // must end with a semicolon.
}

// For methods that are only used inside a component, use lower camel case.
const getHelloWorld = () => {
    return "Hello world."
}

// Components use pointer functions, with types, use upper camel case.
const ExampleComponent:React.FC<ComponentProps> = (props) => {
    const dispatch = useAppDispatch();
    
    // Blank lines are necessary. 
    // Put variables with the same purpose in one segment, and blank lines are processed directly in different segments.
    
    const helloWorldText = getHelloWorld();
    
    return (
        <div>
            <p>Hello world.</p>
            <span>{helloWorldText}</span> {/* Don't use functions in JSX */}
            <p>{props.text}</p>
            <SubComponent /> {/* Components with no content use closed blocks */}
        </div>
    )
}

export default ExampleComponent; // Export main component by default, must end with a semicolon.

Named

  1. Use lower camel case for filenames.
  2. Use upper camel case for the component props interface.
  3. Use upper camel case for the component.

Code Indentation

Requires 4 spaces for indentation. You can use Prettier to manage code formatting more easily.

Complexity Control

We try to avoid a component being too complex, so we can split it into smaller components as much as possible.

{
  "tabWidth": 4
}

Interface

We enforce the following rules:

  1. The default exported interface must be located in the model folder and be a separate file.
  2. All decoupled interfaces must be exported.

The main interface files are placed in a subdirectory of the /src/model directory, and are named with the .ts suffix. Statements other than interfaces must not appear in the file.

// src/model/response/index.ts

// Encapsulates a uniform data type with a generic which has a default value of any.
export default interface Response<T = any> { 
	code: number;
	msg: string;
	data?: T;
	error?: string;
}
// src/model/base/user.ts

// The Base interface must be the most complete.
export default interface User {
    name: string;
	password: string;
    age: number;
    birthday: Date;
}
// src/model/response/userResponse.ts
import User from "~/model/base/user";

// No `Response` nesting
type UserResponse = Omit<User, "password">
export default UserResponse;

Practical Application:

// ...
import Response from "~/model/response";
import UserResponse from "~/model/response/userResponse";
// ...

const apiResult:Response<UserResponse> = useApiResult();
// ...