---
# System prepended metadata

title: 1. Cấu trúc react

---

# 1. Cấu trúc react
## 1.1. Cấu trúc Project
```
- public: thư mục chứa resources của client
	- libs: thư viện ngoài
  - assets
  	- images
    - json
    - js
    - css
    - locales
    	- en
      - vi
  - index.html
  - favicon.ico
- src: thư mục chứa source code
	- assets: các tài nguyên import trực tiếp vào Project
  - styles: thư mục chứa style của Project
  	- [Module1].[scss/css]
    - ...
  - application:
    - configs: các cấu hình để khởi động Project, External Libs
    	- Constants.[js/ts/tsx]: các hằng số của Project
    	- apis: danh sách các api của Project
      	- [Module1]Api.[js/ts]
        - index.[js/ts/tsx]: export các api
    	- routers: mapping các url và pages
      	- index.[js/ts/tsx]: export các router
    - services: chứa business của application
  		- hooks: các thành phần tái sử dụng business, flow
    	- providers: cung cấp các thành phần sử dụng cho việc control flow
    - pages: các trang dùng chung như Login, NotFound, ...
    - index.[js/ts/tsx]: export các thành phần ra bên ngoài
  - components: các thành phần tái sử dụng
  - modules: các chức năng của Project dưới dạng các Modules
  	- [Module1]
    	- [Child-Module1]
    - ...
  - helpers: các tiện tích tái sử dụng dưới dạng function
  - index.[js/ts/tsx]: application startup
- package.json
- ....
```
## 1.2. Cấu trúc Module trong Project
```
- [Module]
  - [Child-Module]
    - configs: các thành phần cấu hình của Module
    	- Constants.[js/ts/tsx]: các hằng số của Module
      - defs:
        - Model
        - ColumnDef
        - ...
    - components: các thành phần tái sử dụng của Module
    - services:
    	- hooks: các thành phần tái sử dụng business, flow của Local Module
      - providers: các Local Provider của Module
    - pages: các trang của Module
    - index.[js/ts/tsx]: export các phần ra bên ngoài
```
> Ví dụ cho Module System > Menu
> {.is-info}
```
- src
	- styles
  	- system
    	- Menu.scss
	- application
  	- configs
  		- Constants.ts
    	-	apis
      	- system
      		- MenuApi.ts
        - index.ts
  - modules
  	- system
    	- menu
      		- configs
          	- defs
              - MenuColDef.ts
          		- MenuCreateModel.ts
              - IMenuProp.ts
              - IMenuItem.ts
        	- components
          	- MenuCreateForm.tsx
          - pages
            - MenuList.tsx
      	  - index.tsx
```
> Ví dụ style Module Menu.scss với .[Tên Module] là prefix.
{.is-warning}
```scss=
// Menu.scss
.menu-list {
  background: #ffffff;
 	padding: 10px;
}

.menu-create-form {
  .form-title {
    font-size: 14px;
    font-weight: bold;
  }
}
```
          
```typescript=
// MenuApi.ts
import { APP_API_PATH } from '~/application/configs/';
export const MENU_LIST_API = APP_API_PATH + '/menu/index';

// index.tsx
import IMenuProp from '../configs/IMenuProp.tsx';
import IMenuItem from '../configs/IMenuItem.tsx';
import MenuList from '../pages/MenuList.tsx';
export {
	IMenuProp,
  IMenuItem,
	MenuList
};
```
    
> Các Module khi phụ thuộc nhau chỉ Import từ file index[.js/ts/tsx]
> {.is-warning}
```javascript=
// "utils/index.ts"
const NotifyHelper = {
  notify: () => {...};
};
export {
	NotifyHelper
};
// Import full path from file "modules/user/pages/create.tsx"
import { NotifyHelper } from '~/helpers';

const CreatePage = () => {
  //...
};

export default CreatePage;
```

## 1.3. Cấu trúc File
### Header
Import theo Thứ tự:
- Các thư viện cài trong package.json
- Các Custom Component
- CSS/SCSS/assets
### Body
Khai báo main Class/Funtion
- Xử lý Prop, State, Ref
- Xử lý Effect
- Xử lý Event: onClick, onCreated...
- Xử lý Render
### Footer
Export: Luôn có 1 default export. Có thể export nhiều đối tượng biến, hàm.
- export default
- export {}

> Tham khảo [Pattern](/tech-docs/react/patterns) để viết Component tốt hơn.
> {.is-warning}

# 2. Quy tắc đặt tên
> - Interface: Thêm tiền tố "I" vào trước tên Component. Ví dụ: IMenuProp.ts,...
> - Type: Thêm tiền tố "T" vào trước tên Component. Ví dụ: TMenu.tsx
> - Tên Folder: CamelCase. Ví dụ: system > userManagement
> - Tên File: PascalCase. Ví dụ: UserManagement.tsx
> - Tên Class / Function Export: PascalCase. Ví dụ: MenuList, UserList...
> - Tên hook: CamelCase. Ví dụ: useCustomHook...
> - Tên hằng: ALL_UPPER_CASE. Ví dụ: MENU_API, PI,...
> - Tên variable, funtion local: CamelCase. Ví dụ: menus, menuStore, renderMenuList...
> - Tên file trong CSS/SCSS: PascalCase. Ví dụ: Menu.scss
> - Tên class trong CSS/SCSS: KebabCase. Ví dụ: .menu-list...

# 3. Common
## 3.1. Prop is Immutable: Không thay đổi dữ liệu truyền vào dưới dạng Prop. Khi cần đổi dữ liệu, Clone dữ liệu sử dụng Lodash(cloneDeep, assign) và cập nhật lại State.
- Bad
```typescript=
const FilterMenu = (menus: IMenuItem[]) => {
 return _.filter(menus, function(menu: IMenuItem) {
   if (_.isUndefined(menu.name) || _.isNull(menu.name)) {
   		menu.name = 'Awesome';
   }
   return _.isEqual(menu.hasRight, true); 
 });
}
```
- Good
```typescript=
// Khi đổi dữ liệu của Prop menus, các hàm có sử dụng biến này sẽ có nguy cơ bị lỗi
const FilterMenu = (menus: IMenuItem[]) => {
 const resultMenus: IMenuItem[] = [];
 // Duyệt qua các phần tử của Menu
 _.forEach(menus, function(menu: IMenuItem) {
  if (_.isEqual(menu.hasRight, true)) {
    // Tạo Clone menu
    let cloneMenu: IMenuItem = _.cloneDeep(menu);
    // Có thể sử dụng toán tử ... để tối ưu
    // let cloneMenu: IMenuItem = {...menu};
    if (_.isUndefined(menu.name) || _.isNull(menu.name)) {
  		_.assign(cloneMenu, {name: 'Awesome'});
  	}
    // Thêm vào mảng kết quả
    resultMenus.push(cloneMenu);
  }
 }
           
 return resultMenus;
});
}
```

## 3.2. Không nên gộp chung Business vào Component mà tách ra Container (vật chứa) và Custom Hook (flow/business)
- Bad
```typescript=
// Viết Business trong User.tsx
const User = () => {
  const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
  const [isLoadingDev, setIsLoadingDev] = useState(true);
  
  const fetchDevs = async () => {
    console.log("this might take some time....");
    await delay(4000);
    setIsLoadingDev(false);
    console.log("Done!");
  };
  
  useEffect(() => {
    fetchDevs();
  }, []);
  
  // return ...
};
```
- Good
```javascript=
// useLoading.tsx
const useLoading = (action) => {
  const [loading, setLoading] = useState(false);
  const doAction = (...args) => {
    setLoading(true);
    // Xử lý Business
    return action(...args).finally(() => setLoading(false));
  };
  return [doAction, loading];
};
// User.tsx
const User = () => {
  const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
  const fetchDevs = async () => {
    console.log("this might take some time....");
    await delay(4000);
    console.log("Done!");
  };
  const [getDev, isLoadingDev] = useLoading(fetchDevs);
  
  useEffect(() => {
    getDev();
  }, []);
  
  return (
    <>
    	{isLoadingDev && <Loading />}
    </>
  );
};
```
> Tham khảo: [Custom reusable Hooks](https://blog.logrocket.com/advanced-react-hooks-creating-custom-reusable-hooks/)

# 4. Promise, Async/await
## 4.1. Sử dụng Axios.all hoặc Promise.all để call nhiều API, try..catch nếu xài async/await.
- Bad
```typescript=
const User = () => {
  const fetchData = async () => {
    const users = await axios.get('/api/users');
    const rights = await axios.get('/api/rights');
    return { users, rights };
  };
  //...
  return (
    <Grid />
  );
};
```
- Good
```typescript=
const User = () => {
  const fetchData = async () => {
    try {
      const [resUser, resRight] = await axios.all([
        axios.get('/api/users'),
        axios.get('/api/rights')
      ]);
      return { users: resUser?.data, rights: resRight?.data };
    } catch (ex: Exception) {
      NotifyUtil.notify(ex.message);
    }
  };
  //...
  return (
    <Grid />
  );
};
```