Skip to content

Instantly share code, notes, and snippets.

@Doko-Demo-Doa
Created November 2, 2021 09:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Doko-Demo-Doa/498cf060f90192bffc4434b6f7927098 to your computer and use it in GitHub Desktop.
Save Doko-Demo-Doa/498cf060f90192bffc4434b6f7927098 to your computer and use it in GitHub Desktop.
JS/React convention
title sidebar_label
JavaScript/React
JavaScript/React

Sau đây là quy ước code JavaScript chung của team, áp dụng chủ yếu cho phía Frontend / Mobile React Native.

  1. Chỉ sử dụng phiên bản Node LTS mới nhất (hiện là 14). Nếu sử dụng nvm thì lện cài đặt là:
npm install --lts
  1. Cài đặt node-gyp dạng global package.

1. Tên biến component:

Tên biến component phải là dạng camelCase

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

// Sai
import ReservationCard from './ReservationCard';

// OK
const reservationItem = <ReservationCard />;

// Sai
const ReservationItem = <ReservationCard />;

2. Cấu trúc thư mục

Có 2 cách sắp xếp:

Cách đơn giản

Cách này khá cơ bản, đơn giản nhưng đủ chức năng, cấu trúc cây thư mục trông như sau:

├── datasource
├── assets
│   ├── fonts
│   ├── icons
│   ├── images
│   └── styles
├── common
├── components
│   ├── button
│   ├── header
│   │   ├── header-button
│   │   └── searchbox
│   ├── logo
│   └── swtich
├── redux
├── routes
│   ├── authen
│   │   ├── products
│   │   ├── purchase-request
│   │   └── successful
│   └── guest
│       └── login
│           └── login-form
├── stores
└── utils

Dù có thể Redux sẽ không được áp dụng, nhưng vì độ phổ biến của nó và hệ thống tài liệu xuất sắc, cây thư mục trên vẫn có tên Redux.

  • redux: Chứa các module sử dụng cho redux, bao gồm file redux-instance.ts để ở ngoài làm file config và 1 thư mục tên features bên trong để đưa logic, slices của redux-toolkit vào, bên trong cũng có types.js để chứa các Type của action (có export ra).

  • datasource: Chứa các file liên quan đến việc gọi web api. File gốc sẽ là api.js hoặc api.ts, tất cả các interceptor sẽ được đặt cùng folder. Nguồn lấy data có thể là từ remote hoặc local (từ server ngoài hoặc từ sqlite trong máy tính, localstorage,...).

  • assets: Chứa các resource file như ảnh, icon,...

  • common: Chứa các file utils để tái sử dụng nhiều lần, file dịch đa ngôn ngữ, file tổng hợp màu sắc, và các config được định nghĩa sẵn.

3. Về import

Thứ tự import sẽ là:

  • React / first party lib
  • Các lib bên thứ 3
  • Các file tự viết, trong đó cũng theo thứ tự:
    • Component
    • Utils
    • Redux / State management
    • Typing (TypeScript)
    • CSS
  • require (nếu phải dùng)

3.1. React luôn được import đầu tiên

Nếu dùng babel 6 đổ về trước hoặc có dùng module import từ React).

Đúng:

:::tip Đúng

import React, {useEffect} from 'react';
import {StatusBar, LogBox, AppState} from 'react-native';
import {Provider} from 'react-redux';
...

:::

:::danger Sai

import {Provider} from 'react-redux';
import React, {useEffect} from 'react';
...

:::tip Đúng

3.2. Khi sử dụng import alias, sử dụng ~ hoặc @

Một số framework cho phép ta config babel / webpack để có thể import dạng absolute lấy theo alias của project. Do đó ta có thể import theo kiểu:

import {store, persistor} from '~/redux/redux-instance';
import {AppNavigator} from '~/navigations/stack-navigator';

Dấu alias luôn lấy là ~ hoặc @ tuỳ dự án.

4. Mỗi module có một nhiệm vụ và không trộn lẫn

Ví dụ, module static-data chỉ chứa các biến constant, các giá trị tĩnh. Không chứa logic tính toán ngoài:

:::tip Đúng

export const PackageTypes = {
  FLEXIBLE: 0,
  COMPREHENSIVE: 1,
  OVERSEE: 2,
};

export const PlaceTypes = {
  ONLINE: 0,
  OFFLINE: 1,
};

export const MaterialType = {
  QUIZ: 11,
  SLIDE: 10,
};

:::

:::danger Sai

export const PackageTypes = {
  FLEXIBLE: 0,
  COMPREHENSIVE: 1,
  OVERSEE: 2,
};

export const PlaceTypes = {
  ONLINE: 0,
  OFFLINE: 1,
};

export functiion getNoteExpandedSheetHeight {
  return (
    WINDOW_HEIGHT - // Size tổng
    getStatusBarHeight() -
    VRM_ROOM_HEADER_HEIGHT -
    (Platform.OS === 'ios' ? BOTTOMSHEET_HANDLE_HEIGHT : -2)
  );
}

:::

5. Chỉ dùng letconst

Nhằm tránh các tác dụng phụ, các closure ngoài ý muốn trong JavaScript / TypeScript. Ta chỉ sử dụng let và const. let khi ta cần assign lại biến về sau và const khi biến đó không cần assign.

:::tip Đúng

const a = 1;
let b = 'test';
b = 'test 2';

:::

:::danger Sai

var a = 1;

:::

6. Áp dụng các kĩ thuật lồng / bao behavior

Với những component có đặc điểm chung về hành vi nhưng khác về giao diện, có 2 kĩ thuật phổ biến với React là:

Tiêu biểu là component connect mà ta vẫn thường dùng với Redux. Ta hoàn toàn có thể làm một component tương tự.

function logProps(WrappedComponent) {
  return class extends React.Component {
    componentDidUpdate(prevProps) {
      console.log('Current props: ', this.props);
      console.log('Previous props: ', prevProps);
    }
    render() {
      return <WrappedComponent {...this.props} />;
    }
  }
}

Là kĩ thuật chia sẻ code giữa các component React sử dụng prop mà value của nó là function (hay còn gọi là "function as a child"):

<DataProvider render={data => (
  <h1>Hello {data.target}</h1>
)}/>

7. DRY

DRY có nghĩa là __D__on't __R__epeat __Y__ourself - đừng lặp lại. Mục đích của việc lập trình là không làm lặp lại những gì máy có thể làm được. Do đó ta cũng cần tránh lặp lại code, copy paste vô tội vạ.

Ví dụ để lặp lại một component 5 lần thì thay vì:

<View>
  <ComponentDemo>
  <ComponentDemo>
  <ComponentDemo>
  <ComponentDemo>
  <ComponentDemo>
</View>

Ta viết:

<View>
  {[0, 1, 2, 3, 4].map(() => <ComponentDemo>)}
</View>

Hoặc tuỳ ngữ cảnh, cũng có thể viết:

// (N) là số phần tử
<View>
  {Array(N).fill().map(() => <ComponentDemo>)}
</View>

8. Dùng async await thay vì then và catch

asyncawait đã có từ ES6 và đã hỗ trợ rất tốt các framework hiện nay. Ưu tiên sử dụng do cách viết gần với luồng code bình thường và có thể viết unit test dễ dàng hơn.

9. Tránh dùng các từ khoá, kí tự đặc biệt làm key object JSON

Việc dùng các kí tự đặc biệt sẽ gây khó khăn cho thao tác đọc / ghi và xử lý JSON về sau. Bao gồm:

  • Kí tự đặc biệt như: !@#$%^&*()
  • Số (trừ một số trường hợp đặc biệt)
  • Các từ nằm trong từ khoá của ngôn ngữ lập trình: class, default, get, set

Đúng:

{
  "color": "green"
}

Sai:

{
  "$%%@&^": "green"
}

10. Với SCSS dạng module (import file SCSS vào JS/TS), phải import dưới cùng

:::tip Đúng

import React from "react";
import CssColors from "~/assets/styles/_colors.module.scss";
import { EventBus } from "~/helpers/event-bus";

import "~/components/color-picker/color-picker.scss";
...

:::

:::danger Sai

import React from "react";

import "~/components/color-picker/color-picker.scss";

import CssColors from "~/assets/styles/_colors.module.scss";
import { EventBus } from "~/helpers/event-bus";
...

:::

11. Với SCSS dạng module, khai báo và export để dùng cho JS/TS

$color-black-primary: #131416;
@import "./_colors.scss";

:export {
  colorBlackPrimary: $color-black-primary;
}

Sau đó có thể sử dụng:

import React from "react";
import { EventBus } from "~/helpers/event-bus";
import CssColors from "~/assets/styles/_colors.module.scss";

import "~/components/color-picker/color-picker.scss";

const COLOR_SEED = [
  CssColors.colorBlackPrimary,
  CssColors.colorGrassGreen,
  CssColors.colorRed,
  CssColors.colorWhite,
];

export const ColorPicker: React.FC = () => {
  return (
    <div className="color-picker">
      {COLOR_SEED.map((n, id) => (
        <div
          className="orb"
          style={{ backgroundColor: n }}
          key={id}
          onClick={() => EventBus.emit("changeColor", { color: n })}
        />
      ))}
    </div>
  );
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment