Hello everyone, I'm Casson.
React
One of the great advantages of the technology stack is that the community is prosperous, and you can basically find the corresponding open source library for the functions you need to implement in your business.
But prosperity also has a downside - there are so many options to achieve the same function, which one to choose?
This article will introduce a 12.7k open source project - Bulletproof React
This project provides advice on all aspects of building a clean, powerful, and extensible front-end project architecture .
Welcome to join the human high-quality front-end framework group , with flying
What is Bulletproof React
Bulletproof React
is different from our common scaffolding (such as CRA
), which is used to create a new project based on the template .
The former contains a complete React
full-stack forum project:
The author uses this project as an example to show 13 aspects related to the project architecture , such as:
- How to organize file directories
- What is the recommendation for engineering configuration?
- How to standardize when writing business components
- How to do state management
-
API
How layers are designed - and many more......
Due to limited space, this article introduces some of them.
Do you agree with these views?
How file directories are organized
The project recommends the following directory format:
src
|
+-- assets # 静态资源
|
+-- components # 公共组件
|
+-- config # 全局配置
|
+-- features # 特性
|
+-- hooks # 公用hooks
|
+-- lib # 二次导出的第三方库
|
+-- providers # 应用中所有providers
|
+-- routes # 路由配置
|
+-- stores # 全局状态stores
|
+-- test # 测试工具、mock服务器
|
+-- types # 全局类型文件
|
+-- utils # 通用工具函数
Among them, the difference between the features
directory and the components
directory is:
components
stores global public components, and features
stores business-related features .
For example, if I want to develop a comment module, comment is a feature, and all content related to it exists in the features/comments
directory.
An input box is required in the comment module. The general component of the input box comes from the components
directory.
All feature-related content will converge to the features
directory, including:
src/features/xxx-feature
|
+-- api # 与特性相关的请求
|
+-- assets # 与特性相关的静态资源
|
+-- components # 与特性相关的组件
|
+-- hooks # 与特性相关的hooks
|
+-- routes # 与特性相关的路由
|
+-- stores # 与特性相关的状态stores
|
+-- types # 与特性相关的类型申明
|
+-- utils # 与特性相关的工具函数
|
+-- index.ts # 入口
All content exported by the feature can only be called through a unified entry, such as:
import { CommentBar } from "@/features/comments"
instead of:
import { CommentBar } from "@/features/comments/components/CommentBar
This can be achieved by configuring ESLint
:
{
rules: {
'no-restricted-imports': [
'error',
{
patterns: ['@/features/*/*'],
},
],
// ...其他配置
}
}
Compared with storing feature-related content in the global directory in a flat form (for example, storing feature hooks in the global hooks directory), using the features
directory as a collection of related codes can effectively prevent project volume The situation where the code organization is chaotic after the increase.
How to do state management
Not all states in the project need to be stored in a centralized store , and need to be treated differently according to the state type.
component status
For the local state of a component, if only the component itself and its descendant components need this part of the state, you can save them with useState
or useReducer
.
application status
States related to application interaction, such as opening pop-up windows , notifications , changing night mode , etc., should follow the principle of keeping the state as close as possible to the component that uses it , and not define any state as a global state .
Taking the example project in Bulletproof React
as an example, first define the notification-related status :
// bulletproof-react/src/stores/notifications.ts
export const useNotificationStore = create<NotificationsStore>((set) => ({
notifications: [],
addNotification: (notification) =>
set((state) => ({
notifications: [...state.notifications, { id: nanoid(), ...notification }],
})),
dismissNotification: (id) =>
set((state) => ({
notifications: state.notifications.filter((notification) => notification.id !== id),
})),
}));
Then reference useNotificationStore
anywhere notification-related state is used, for example:
// bulletproof-react/src/components/Notifications/Notifications.tsx
import { useNotificationStore } from '@/stores/notifications';
import { Notification } from './Notification';
export const Notifications = () => {
const { notifications, dismissNotification } = useNotificationStore();
return (
<div
>
{notifications.map((notification) => (
<Notification
key={notification.id}
notification={notification}
onDismiss={dismissNotification}
/>
))}
</div>
);
};
The state management tool used here is zustand
, and there are many other options:
-
context
+hooks
-
redux
+redux toolkit
-
mobx
-
constate
-
jotai
-
recoil
-
xstate
These schemes have their own characteristics, but they are all designed to handle application state .
server cache status
For the data requested from the server and cached in the front-end, although the above tools for processing application status can be used, the server-side cache status , compared with the application status , also involves issues such as cache invalidation and serialized data .
So it is best to use special tools to deal with, such as:
-
react-query - REST
+GraphQL
-
swr - REST
+GraphQL
-
apollo client
-GraphQL
-
urql
-GraphQl
form state
Form data needs to be distinguished from controlled and uncontrolled , and the form itself still has a lot of logic to process (such as form validation ), so it is also recommended to use a special library to handle this part of the state, such as:
-
React Hook Form
-
Formik
-
React Final Form
URL status
URL
Status includes:
-
url params
(/app/${dynamicParam}) -
query params
(/app?dynamicParam=1)
This part of the state is usually handled by the routing library, such as react-router-dom
.
Summarize
This article has selected some of the recommended solutions in Bulletproof React
. Do you have any opinions that you agree with?
Welcome to exchange best practices in project architecture in the comments section.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。