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:
- Use lower camel case for all the variables.
- Use lower camel case for all the file names.
- Use lower camel case for functions, such as
getUser(). - Use lower camel case for interfaces which are not exported.
- Use upper camel case for interfaces which are exported.
- Use upper camel case for components, such as
<Component/>. - 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:
- for variables representing site configuration, we use
siteConfig, in all cases. - for variables representing usernames, we use
username, in all cases. - for functions that represent getting site configuration, we use
getSiteConfig. - for the interface representing the type of the return value of the Get Site Config interface, we use
SiteConfigResponse. - for the interface representing the request value to query the user interface, we use
UserGetPayload. - for the interface representing the parameters required to upload an image, we use
ImageUploadPayload. - for an interface representing the type of component parameter, we use
ComponentProps.
We also have some hard requirements:
- The start of the curly brace must immediately follow the left-hand, without a newline.
- The use of the
varandfunctionkeywords in code is prohibited under any circumstances. - The code must be formatted by Prettier, and individuals are not allowed to modify the Prettier configuration file without permission.
- 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.
- We want to have only one main component in a file, and the split widgets are placed in different files.
- Each component uses a separate sub folder, the default reference is located in
index.tsx. - Use lower camel case for subdirectory names of components.
- We encourage the use of functional components.
- 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
- Use lower camel case for filenames.
- Use upper camel case for the component props interface.
- 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:
- The default exported interface must be located in the model folder and be a separate file.
- 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();
// ...
