Hướng dẫn dùng protypes JavaScript

Những từ mang nặng tính kĩ thuật sẽ được giữ nguyên để tránh sự nhầm lẫn. Tuy nhiên có thể bạn sẽ thích một phiên bản thuần Việt hơn, cứ thoải mái đóng góp nhé ;)

Mục lục

  1. Những luật cơ bản
  2. So sánh Class vs
    // tệ
    import Footer from './Footer/Footer';
    
    // tệ
    import Footer from './Footer/index';
    
    // tốt
    import Footer from './Footer';
    1 vs stateless
  3. Mixins
  4. Đặt tên
  5. Khai báo
  6. Căn chỉnh mã nguồn
  7. Dấu nháy đơn và nháy kép
  8. Khoảng trắng
  9. Props
  10. Refs
  11. Dấu ngoặc đơn
  12. Thẻ
  13. Phương thức
  14. Cách sắp xếp hàm
  15. Thuộc tính
    // tệ
    import Footer from './Footer/Footer';
    
    // tệ
    import Footer from './Footer/index';
    
    // tốt
    import Footer from './Footer';
    2

Những luật cơ bản

  • Chỉ chứa một React Component trong 1 file.
  • Tuy nhiên, những component có khả năng sử dụng lại(Stateless Component, hoặc Pure Components) có thể viết chung một file. eslint:
    // tệ
    import Footer from './Footer/Footer';
    
    // tệ
    import Footer from './Footer/index';
    
    // tốt
    import Footer from './Footer';
    3.
  • Luôn luôn sử dụng cú pháp JSX.  - Không sử dụng
    // tệ
    import Footer from './Footer/Footer';
    
    // tệ
    import Footer from './Footer/index';
    
    // tốt
    import Footer from './Footer';
    4 chung với cú pháp JSX.
  • Nếu Component có state hoặc refs, nên sử dụng

    // tệ
    import Footer from './Footer/Footer';
    
    // tệ
    import Footer from './Footer/index';
    
    // tốt
    import Footer from './Footer';
    6 thay vì
    // tệ
    import Footer from './Footer/Footer';
    
    // tệ
    import Footer from './Footer/index';
    
    // tốt
    import Footer from './Footer';
    1. eslint:
    // tệ
    import Footer from './Footer/Footer';
    
    // tệ
    import Footer from './Footer/index';
    
    // tốt
    import Footer from './Footer';
    8
    // tệ
    import Footer from './Footer/Footer';
    
    // tệ
    import Footer from './Footer/index';
    
    // tốt
    import Footer from './Footer';
    9

    // tệ
    const Listing = React.createClass({
      // ...
      render() {
        return <div>{this.state.hello}</div>;
      }
    });
    
    // tốt
    class Listing extends React.Component {
      // ...
      render() {
        return <div>{this.state.hello}</div>;
      }
    }

    Và nếu trong Component không có state hoặc refs, nên sử dụng khai báo hàm (không phải arrow function) thay vì class:

    // tệ
    class Listing extends React.Component {
      render() {
        return <div>{this.props.hello}</div>;
      }
    }
    
    // tệ (dựa vào tên hàm để suy luận thì rất đau đầu)
    const Listing = ({ hello }) => (
      <div>{hello}</div>
    );
    
    // tốt
    function Listing({ hello }) {
      return <div>{hello}</div>;
    }

Mixins

  • Chi tiết vì sao không nên sử dụng mixins.

Mixins tạo ra các implicit dependencies(phụ thuộc ngầm), gây ra xung đột tên và tăng độ phức tạp. Có thể thay thế mixins bằng components, higher-order components, hoặc các utility modules(gói tiện ích).

Đặt tên

  • Phần mở rộng(extensions): Sử dụng phần mở rộng

    // tệ
    export default function withFoo(WrappedComponent) {
      return function WithFoo(props) {
        return <WrappedComponent {...props} foo />;
      }
    }
    
    // tốt
    export default function withFoo(WrappedComponent) {
      function WithFoo(props) {
        return <WrappedComponent {...props} foo />;
      }
    
      const wrappedComponentName = WrappedComponent.displayName
        || WrappedComponent.name
        || 'Component';
    
      WithFoo.displayName = `withFoo(${wrappedComponentName})`;
      return WithFoo;
    }
    0 cho React Components.

  • Tên file: Sử dụng chuẩn PascalCase cho tên file. Ví dụ:

    // tệ
    export default function withFoo(WrappedComponent) {
      return function WithFoo(props) {
        return <WrappedComponent {...props} foo />;
      }
    }
    
    // tốt
    export default function withFoo(WrappedComponent) {
      function WithFoo(props) {
        return <WrappedComponent {...props} foo />;
      }
    
      const wrappedComponentName = WrappedComponent.displayName
        || WrappedComponent.name
        || 'Component';
    
      WithFoo.displayName = `withFoo(${wrappedComponentName})`;
      return WithFoo;
    }
    1.

  • Tên tham chiếu(Reference Naming): Sử dụng PascalCase cho React components và dùng camelCase cho các đối tượng(instances) của chúng. eslint:

    // tệ
    export default function withFoo(WrappedComponent) {
      return function WithFoo(props) {
        return <WrappedComponent {...props} foo />;
      }
    }
    
    // tốt
    export default function withFoo(WrappedComponent) {
      function WithFoo(props) {
        return <WrappedComponent {...props} foo />;
      }
    
      const wrappedComponentName = WrappedComponent.displayName
        || WrappedComponent.name
        || 'Component';
    
      WithFoo.displayName = `withFoo(${wrappedComponentName})`;
      return WithFoo;
    }
    2

    // tệ
    import reservationCard from './ReservationCard';
    
    // tốt
    import ReservationCard from './ReservationCard';
    
    // tệ
    const ReservationItem = <ReservationCard />;
    
    // tốt
    const reservationItem = <ReservationCard />;

  • Đặt tên Component: Sử dụng tên file trùng với tên component. Ví dụ:

    // tệ
    export default function withFoo(WrappedComponent) {
      return function WithFoo(props) {
        return <WrappedComponent {...props} foo />;
      }
    }
    
    // tốt
    export default function withFoo(WrappedComponent) {
      function WithFoo(props) {
        return <WrappedComponent {...props} foo />;
      }
    
      const wrappedComponentName = WrappedComponent.displayName
        || WrappedComponent.name
        || 'Component';
    
      WithFoo.displayName = `withFoo(${wrappedComponentName})`;
      return WithFoo;
    }
    1 nên có tên tham chiếu là
    // tệ
    export default function withFoo(WrappedComponent) {
      return function WithFoo(props) {
        return <WrappedComponent {...props} foo />;
      }
    }
    
    // tốt
    export default function withFoo(WrappedComponent) {
      function WithFoo(props) {
        return <WrappedComponent {...props} foo />;
      }
    
      const wrappedComponentName = WrappedComponent.displayName
        || WrappedComponent.name
        || 'Component';
    
      WithFoo.displayName = `withFoo(${wrappedComponentName})`;
      return WithFoo;
    }
    4. Tuy nhiên, đối với các component gốc của một thư mục, hãy sử dụng
    // tệ
    export default function withFoo(WrappedComponent) {
      return function WithFoo(props) {
        return <WrappedComponent {...props} foo />;
      }
    }
    
    // tốt
    export default function withFoo(WrappedComponent) {
      function WithFoo(props) {
        return <WrappedComponent {...props} foo />;
      }
    
      const wrappedComponentName = WrappedComponent.displayName
        || WrappedComponent.name
        || 'Component';
    
      WithFoo.displayName = `withFoo(${wrappedComponentName})`;
      return WithFoo;
    }
    5 làm tên file và sử dụng tên thư mục làm tên component:

    // tệ
    import Footer from './Footer/Footer';
    
    // tệ
    import Footer from './Footer/index';
    
    // tốt
    import Footer from './Footer';

  • Đặt tên Higher-order Component: Sử dụng sự kết hợp của Higher-order component và tên của component đuợc truyền như

    // tệ
    export default function withFoo(WrappedComponent) {
      return function WithFoo(props) {
        return <WrappedComponent {...props} foo />;
      }
    }
    
    // tốt
    export default function withFoo(WrappedComponent) {
      function WithFoo(props) {
        return <WrappedComponent {...props} foo />;
      }
    
      const wrappedComponentName = WrappedComponent.displayName
        || WrappedComponent.name
        || 'Component';
    
      WithFoo.displayName = `withFoo(${wrappedComponentName})`;
      return WithFoo;
    }
    6(tên hiển thị) trên component đuợc tạo ra. Ví dụ component bậc cao
    // tệ
    export default function withFoo(WrappedComponent) {
      return function WithFoo(props) {
        return <WrappedComponent {...props} foo />;
      }
    }
    
    // tốt
    export default function withFoo(WrappedComponent) {
      function WithFoo(props) {
        return <WrappedComponent {...props} foo />;
      }
    
      const wrappedComponentName = WrappedComponent.displayName
        || WrappedComponent.name
        || 'Component';
    
      WithFoo.displayName = `withFoo(${wrappedComponentName})`;
      return WithFoo;
    }
    7, khi truyền một component
    // tệ
    export default function withFoo(WrappedComponent) {
      return function WithFoo(props) {
        return <WrappedComponent {...props} foo />;
      }
    }
    
    // tốt
    export default function withFoo(WrappedComponent) {
      function WithFoo(props) {
        return <WrappedComponent {...props} foo />;
      }
    
      const wrappedComponentName = WrappedComponent.displayName
        || WrappedComponent.name
        || 'Component';
    
      WithFoo.displayName = `withFoo(${wrappedComponentName})`;
      return WithFoo;
    }
    8 sẽ tạo ra một component với
    // tệ
    export default function withFoo(WrappedComponent) {
      return function WithFoo(props) {
        return <WrappedComponent {...props} foo />;
      }
    }
    
    // tốt
    export default function withFoo(WrappedComponent) {
      function WithFoo(props) {
        return <WrappedComponent {...props} foo />;
      }
    
      const wrappedComponentName = WrappedComponent.displayName
        || WrappedComponent.name
        || 'Component';
    
      WithFoo.displayName = `withFoo(${wrappedComponentName})`;
      return WithFoo;
    }
    6 của
    // tệ
    <MyComponent style="fancy" />
    
    // tệ
    <MyComponent className="fancy" />
    
    // tốt
    <MyComponent variant="fancy" />
    0.

    Tại sao? Vì

    // tệ
    export default function withFoo(WrappedComponent) {
      return function WithFoo(props) {
        return <WrappedComponent {...props} foo />;
      }
    }
    
    // tốt
    export default function withFoo(WrappedComponent) {
      function WithFoo(props) {
        return <WrappedComponent {...props} foo />;
      }
    
      const wrappedComponentName = WrappedComponent.displayName
        || WrappedComponent.name
        || 'Component';
    
      WithFoo.displayName = `withFoo(${wrappedComponentName})`;
      return WithFoo;
    }
    6 của component có thể đuợc sử dụng bởi những công cụ phát triển hoặc trong các thông báo lỗi, và có một giá trị mà thể hiện rõ mối quan hệ này sẽ giúp chúng hiểu rõ chuyện gì đang xảy ra.

    // tệ
    export default function withFoo(WrappedComponent) {
      return function WithFoo(props) {
        return <WrappedComponent {...props} foo />;
      }
    }
    
    // tốt
    export default function withFoo(WrappedComponent) {
      function WithFoo(props) {
        return <WrappedComponent {...props} foo />;
      }
    
      const wrappedComponentName = WrappedComponent.displayName
        || WrappedComponent.name
        || 'Component';
    
      WithFoo.displayName = `withFoo(${wrappedComponentName})`;
      return WithFoo;
    }

  • Đặt tên Props: Tránh sử dụng tên props của DOM Component cho mục đích khác.

    Tại sao? Mọi nguời mong đợi props như

    // tệ
    <MyComponent style="fancy" />
    
    // tệ
    <MyComponent className="fancy" />
    
    // tốt
    <MyComponent variant="fancy" />
    2 và
    // tệ
    <MyComponent style="fancy" />
    
    // tệ
    <MyComponent className="fancy" />
    
    // tốt
    <MyComponent variant="fancy" />
    3 có ý nghĩa riêng. Việc thay đổi mục đích sử dụng của API gốc làm cho mã khó đọc và khó bảo trì hơn, thậm chí có thể gây ra lỗi.

    // tệ
    <MyComponent style="fancy" />
    
    // tệ
    <MyComponent className="fancy" />
    
    // tốt
    <MyComponent variant="fancy" />

Khai báo

  • Không nên sử dụng

    // tệ
    export default function withFoo(WrappedComponent) {
      return function WithFoo(props) {
        return <WrappedComponent {...props} foo />;
      }
    }
    
    // tốt
    export default function withFoo(WrappedComponent) {
      function WithFoo(props) {
        return <WrappedComponent {...props} foo />;
      }
    
      const wrappedComponentName = WrappedComponent.displayName
        || WrappedComponent.name
        || 'Component';
    
      WithFoo.displayName = `withFoo(${wrappedComponentName})`;
      return WithFoo;
    }
    6 để đặt tên cho các Components. Thay vào đó, đặt tên cho các Components bằng references(tham chiếu).

    // tệ
    export default React.createClass({
      displayName: 'ReservationCard',
      // một số thứ khác
    });
    
    // tốt
    export default class ReservationCard extends React.Component {
    }

Căn chỉnh mã nguồn

  • Căn chỉnh cho cú pháp JSX. eslint: react/jsx-closing-bracket-location react/jsx-closing-tag-location

    // tệ
    <Foo superLongParam="bar"
         anotherSuperLongParam="baz" />
    
    // tốt
    <Foo
      superLongParam="bar"
      anotherSuperLongParam="baz"
    />
    
    // Nếu props phù hợp trong một dòng thì giữ nó trên cùng một dòng
    <Foo bar="bar" />
    
    // Component con được thụt lề bình thường
    <Foo
      superLongParam="bar"
      anotherSuperLongParam="baz"
    >
      <Quux />
    </Foo>

Dấu nháy đơn và nháy kép

  • Luôn luôn sử dụng dấu ngoặc kép (

    // tệ
    <MyComponent style="fancy" />
    
    // tệ
    <MyComponent className="fancy" />
    
    // tốt
    <MyComponent variant="fancy" />
    5) cho các thuộc tính JSX, nhưng dấu nháy đơn (
    // tệ
    <MyComponent style="fancy" />
    
    // tệ
    <MyComponent className="fancy" />
    
    // tốt
    <MyComponent variant="fancy" />
    6) cho tất cả các JS khác. Eslint: jsx-quotes

    Tại sao? Vì các thuộc tính HTML thông thường thường sử dụng dấu ngoặc kép thay vì đơn, vì vậy thuộc tính JSX cũng như thế.

    // tệ
    <Foo bar='bar' />
    
    // tốt
    <Foo bar="bar" />
    
    // tệ
    <Foo style={{ left: "20px" }} />
    
    // tốt
    <Foo style={{ left: '20px' }} />

Khoảng trắng

  • Luôn luôn có duy nhất một kí tự space(khoảng trắng) trong thẻ tự đóng. eslint:

    // tệ
    <MyComponent style="fancy" />
    
    // tệ
    <MyComponent className="fancy" />
    
    // tốt
    <MyComponent variant="fancy" />
    7,
    // tệ
    <MyComponent style="fancy" />
    
    // tệ
    <MyComponent className="fancy" />
    
    // tốt
    <MyComponent variant="fancy" />
    8

    // tệ
    <Foo/>
    
    // rất tệ
    <Foo                 />
    
    // tệ
    <Foo
    />
    
    // tốt
    <Foo />

  • Không dùng khoảng trắng giữa giá trị bên trong ngoặc nhọn. eslint:

    // tệ
    <MyComponent style="fancy" />
    
    // tệ
    <MyComponent className="fancy" />
    
    // tốt
    <MyComponent variant="fancy" />
    9

    // tệ
    class Listing extends React.Component {
      render() {
        return <div>{this.props.hello}</div>;
      }
    }
    
    // tệ (dựa vào tên hàm để suy luận thì rất đau đầu)
    const Listing = ({ hello }) => (
      <div>{hello}</div>
    );
    
    // tốt
    function Listing({ hello }) {
      return <div>{hello}</div>;
    }
    0

Props

  • Luôn luôn sử dụng camelCase khi đặt tên prop (camelCase : viết hoa chữa cái đầu của các từ , từ đầu tiên của cụm thì viết thường)

    // tệ
    class Listing extends React.Component {
      render() {
        return <div>{this.props.hello}</div>;
      }
    }
    
    // tệ (dựa vào tên hàm để suy luận thì rất đau đầu)
    const Listing = ({ hello }) => (
      <div>{hello}</div>
    );
    
    // tốt
    function Listing({ hello }) {
      return <div>{hello}</div>;
    }
    1

  • Bỏ giá trị của prop khi nó thực sự rõ ràng là

    // tệ
    export default React.createClass({
      displayName: 'ReservationCard',
      // một số thứ khác
    });
    
    // tốt
    export default class ReservationCard extends React.Component {
    }
    0. eslint:
    // tệ
    export default React.createClass({
      displayName: 'ReservationCard',
      // một số thứ khác
    });
    
    // tốt
    export default class ReservationCard extends React.Component {
    }
    1

    // tệ
    class Listing extends React.Component {
      render() {
        return <div>{this.props.hello}</div>;
      }
    }
    
    // tệ (dựa vào tên hàm để suy luận thì rất đau đầu)
    const Listing = ({ hello }) => (
      <div>{hello}</div>
    );
    
    // tốt
    function Listing({ hello }) {
      return <div>{hello}</div>;
    }
    2

  • Luôn luôn sử dụng prop

    // tệ
    export default React.createClass({
      displayName: 'ReservationCard',
      // một số thứ khác
    });
    
    // tốt
    export default class ReservationCard extends React.Component {
    }
    2 trong thẻ
    // tệ
    export default React.createClass({
      displayName: 'ReservationCard',
      // một số thứ khác
    });
    
    // tốt
    export default class ReservationCard extends React.Component {
    }
    3. Nếu giá trị của thẻ là NULL ,
    // tệ
    export default React.createClass({
      displayName: 'ReservationCard',
      // một số thứ khác
    });
    
    // tốt
    export default class ReservationCard extends React.Component {
    }
    2 có thể là một chuỗi rỗng hoặc
    // tệ
    export default React.createClass({
      displayName: 'ReservationCard',
      // một số thứ khác
    });
    
    // tốt
    export default class ReservationCard extends React.Component {
    }
    3 phải có thuộc tính
    // tệ
    export default React.createClass({
      displayName: 'ReservationCard',
      // một số thứ khác
    });
    
    // tốt
    export default class ReservationCard extends React.Component {
    }
    6. eslint:
    // tệ
    export default React.createClass({
      displayName: 'ReservationCard',
      // một số thứ khác
    });
    
    // tốt
    export default class ReservationCard extends React.Component {
    }
    7

    // tệ
    class Listing extends React.Component {
      render() {
        return <div>{this.props.hello}</div>;
      }
    }
    
    // tệ (dựa vào tên hàm để suy luận thì rất đau đầu)
    const Listing = ({ hello }) => (
      <div>{hello}</div>
    );
    
    // tốt
    function Listing({ hello }) {
      return <div>{hello}</div>;
    }
    3

  • Không dùng các từ "image", "photo", hoặc "picture" trong

    // tệ
    export default React.createClass({
      displayName: 'ReservationCard',
      // một số thứ khác
    });
    
    // tốt
    export default class ReservationCard extends React.Component {
    }
    3
    // tệ
    export default React.createClass({
      displayName: 'ReservationCard',
      // một số thứ khác
    });
    
    // tốt
    export default class ReservationCard extends React.Component {
    }
    2 props. eslint:
    // tệ
    <Foo superLongParam="bar"
         anotherSuperLongParam="baz" />
    
    // tốt
    <Foo
      superLongParam="bar"
      anotherSuperLongParam="baz"
    />
    
    // Nếu props phù hợp trong một dòng thì giữ nó trên cùng một dòng
    <Foo bar="bar" />
    
    // Component con được thụt lề bình thường
    <Foo
      superLongParam="bar"
      anotherSuperLongParam="baz"
    >
      <Quux />
    </Foo>
    0

    Tại sao? Screenreaders đã tự hiểu

    // tệ
    <Foo superLongParam="bar"
         anotherSuperLongParam="baz" />
    
    // tốt
    <Foo
      superLongParam="bar"
      anotherSuperLongParam="baz"
    />
    
    // Nếu props phù hợp trong một dòng thì giữ nó trên cùng một dòng
    <Foo bar="bar" />
    
    // Component con được thụt lề bình thường
    <Foo
      superLongParam="bar"
      anotherSuperLongParam="baz"
    >
      <Quux />
    </Foo>
    1 elements là image(ảnh), vì vậy không cần khai báo thông tin này trong alt

    // tệ
    class Listing extends React.Component {
      render() {
        return <div>{this.props.hello}</div>;
      }
    }
    
    // tệ (dựa vào tên hàm để suy luận thì rất đau đầu)
    const Listing = ({ hello }) => (
      <div>{hello}</div>
    );
    
    // tốt
    function Listing({ hello }) {
      return <div>{hello}</div>;
    }
    4

  • Chỉ sử dụng ARIA roles. eslint:

    // tệ
    <Foo superLongParam="bar"
         anotherSuperLongParam="baz" />
    
    // tốt
    <Foo
      superLongParam="bar"
      anotherSuperLongParam="baz"
    />
    
    // Nếu props phù hợp trong một dòng thì giữ nó trên cùng một dòng
    <Foo bar="bar" />
    
    // Component con được thụt lề bình thường
    <Foo
      superLongParam="bar"
      anotherSuperLongParam="baz"
    >
      <Quux />
    </Foo>
    2 hợp lệ, và không trừu tượng. jsx-a11y/aria-role

    // tệ
    class Listing extends React.Component {
      render() {
        return <div>{this.props.hello}</div>;
      }
    }
    
    // tệ (dựa vào tên hàm để suy luận thì rất đau đầu)
    const Listing = ({ hello }) => (
      <div>{hello}</div>
    );
    
    // tốt
    function Listing({ hello }) {
      return <div>{hello}</div>;
    }
    5

  • Không dùng

    // tệ
    <Foo superLongParam="bar"
         anotherSuperLongParam="baz" />
    
    // tốt
    <Foo
      superLongParam="bar"
      anotherSuperLongParam="baz"
    />
    
    // Nếu props phù hợp trong một dòng thì giữ nó trên cùng một dòng
    <Foo bar="bar" />
    
    // Component con được thụt lề bình thường
    <Foo
      superLongParam="bar"
      anotherSuperLongParam="baz"
    >
      <Quux />
    </Foo>
    3 trong các elements. eslint:
    // tệ
    <Foo superLongParam="bar"
         anotherSuperLongParam="baz" />
    
    // tốt
    <Foo
      superLongParam="bar"
      anotherSuperLongParam="baz"
    />
    
    // Nếu props phù hợp trong một dòng thì giữ nó trên cùng một dòng
    <Foo bar="bar" />
    
    // Component con được thụt lề bình thường
    <Foo
      superLongParam="bar"
      anotherSuperLongParam="baz"
    >
      <Quux />
    </Foo>
    4

    Tại sao ? Sự mâu thuẫn giữa phím tắt và các lệnh bàn phím được những người dùng screenreaders sử dụng làm phức tạp hóa khả năng tiếp cận.

    // tệ
    class Listing extends React.Component {
      render() {
        return <div>{this.props.hello}</div>;
      }
    }
    
    // tệ (dựa vào tên hàm để suy luận thì rất đau đầu)
    const Listing = ({ hello }) => (
      <div>{hello}</div>
    );
    
    // tốt
    function Listing({ hello }) {
      return <div>{hello}</div>;
    }
    6

  • Tránh dùng chỉ số của mảng(index) cho thuộc tính

    // tệ
    <Foo superLongParam="bar"
         anotherSuperLongParam="baz" />
    
    // tốt
    <Foo
      superLongParam="bar"
      anotherSuperLongParam="baz"
    />
    
    // Nếu props phù hợp trong một dòng thì giữ nó trên cùng một dòng
    <Foo bar="bar" />
    
    // Component con được thụt lề bình thường
    <Foo
      superLongParam="bar"
      anotherSuperLongParam="baz"
    >
      <Quux />
    </Foo>
    5, nên sử dụng một unique ID(định danh duy nhất). (why?)

    // tệ
    class Listing extends React.Component {
      render() {
        return <div>{this.props.hello}</div>;
      }
    }
    
    // tệ (dựa vào tên hàm để suy luận thì rất đau đầu)
    const Listing = ({ hello }) => (
      <div>{hello}</div>
    );
    
    // tốt
    function Listing({ hello }) {
      return <div>{hello}</div>;
    }
    7

  • Luôn xác định rõ ràng các defaultProp(thuộc tính mặc định) cho tất cả non-required props(thuộc tính không bắt buộc).

    Tại sao? propTypes được coi như tài liệu, và cung cấp defaultProps , nghĩa là người đọc mã nguồn của bạn không cần phải đoán quá nhiều. Ngoài ra, nó có thể bỏ qua một số kiểm tra kiểu(type checking).

    // tệ
    class Listing extends React.Component {
      render() {
        return <div>{this.props.hello}</div>;
      }
    }
    
    // tệ (dựa vào tên hàm để suy luận thì rất đau đầu)
    const Listing = ({ hello }) => (
      <div>{hello}</div>
    );
    
    // tốt
    function Listing({ hello }) {
      return <div>{hello}</div>;
    }
    8

  • Hạn chế lạm dụng toán tử spread cho việc truyền props

Tại sao? Vì bạn có khả năng truyền props không cần thiết xuống Components. Và với React v15.6.1 trờ lên, bạn cần chuyển các thuộc tính không hợp lệ của HTML sang DOM.

Ngoại lệ:

  • HOCs có thể truyền thẳng props xuống và khai báo propTypes

    // tệ
    class Listing extends React.Component {
      render() {
        return <div>{this.props.hello}</div>;
      }
    }
    
    // tệ (dựa vào tên hàm để suy luận thì rất đau đầu)
    const Listing = ({ hello }) => (
      <div>{hello}</div>
    );
    
    // tốt
    function Listing({ hello }) {
      return <div>{hello}</div>;
    }
    9

  • Sử dụng toán tử spread đối với prop được khai báo rõ ràng. Điều này có thể đặc biệt hữu ích khi test các React component với cấu trúc beforeEach của Mocha.

    // tệ
    import reservationCard from './ReservationCard';
    
    // tốt
    import ReservationCard from './ReservationCard';
    
    // tệ
    const ReservationItem = <ReservationCard />;
    
    // tốt
    const reservationItem = <ReservationCard />;
    0

  • Ghi chú: Nên lọc các props không cần thiết khi có thể. Ngoài ra, sử dụng prop-types-exact để giúp ngăn ngừa lỗi.

    // tệ
    import reservationCard from './ReservationCard';
    
    // tốt
    import ReservationCard from './ReservationCard';
    
    // tệ
    const ReservationItem = <ReservationCard />;
    
    // tốt
    const reservationItem = <ReservationCard />;
    1

Refs

  • Luôn sử dụng hàm gọi lại(callback) cho khai báo ref. eslint:

    // tệ
    <Foo superLongParam="bar"
         anotherSuperLongParam="baz" />
    
    // tốt
    <Foo
      superLongParam="bar"
      anotherSuperLongParam="baz"
    />
    
    // Nếu props phù hợp trong một dòng thì giữ nó trên cùng một dòng
    <Foo bar="bar" />
    
    // Component con được thụt lề bình thường
    <Foo
      superLongParam="bar"
      anotherSuperLongParam="baz"
    >
      <Quux />
    </Foo>
    6

    // tệ
    import reservationCard from './ReservationCard';
    
    // tốt
    import ReservationCard from './ReservationCard';
    
    // tệ
    const ReservationItem = <ReservationCard />;
    
    // tốt
    const reservationItem = <ReservationCard />;
    2

Dấu ngoặc đơn

  • Đóng gói các thẻ JSX trong ngoặc đơn khi chúng kéo dài nhiều dòng. eslint:

    // tệ
    <Foo superLongParam="bar"
         anotherSuperLongParam="baz" />
    
    // tốt
    <Foo
      superLongParam="bar"
      anotherSuperLongParam="baz"
    />
    
    // Nếu props phù hợp trong một dòng thì giữ nó trên cùng một dòng
    <Foo bar="bar" />
    
    // Component con được thụt lề bình thường
    <Foo
      superLongParam="bar"
      anotherSuperLongParam="baz"
    >
      <Quux />
    </Foo>
    7

    // tệ
    import reservationCard from './ReservationCard';
    
    // tốt
    import ReservationCard from './ReservationCard';
    
    // tệ
    const ReservationItem = <ReservationCard />;
    
    // tốt
    const reservationItem = <ReservationCard />;
    3

Thẻ

  • Luôn luôn tự đóng các thẻ(tags) không có con. eslint:

    // tệ
    <Foo superLongParam="bar"
         anotherSuperLongParam="baz" />
    
    // tốt
    <Foo
      superLongParam="bar"
      anotherSuperLongParam="baz"
    />
    
    // Nếu props phù hợp trong một dòng thì giữ nó trên cùng một dòng
    <Foo bar="bar" />
    
    // Component con được thụt lề bình thường
    <Foo
      superLongParam="bar"
      anotherSuperLongParam="baz"
    >
      <Quux />
    </Foo>
    8

    // tệ
    import reservationCard from './ReservationCard';
    
    // tốt
    import ReservationCard from './ReservationCard';
    
    // tệ
    const ReservationItem = <ReservationCard />;
    
    // tốt
    const reservationItem = <ReservationCard />;
    4

  • Nếu Component của bạn có thuộc tính nhiều dòng, hãy đóng thẻ đó trên 1 dòng mới. eslint:

    // tệ
    <Foo superLongParam="bar"
         anotherSuperLongParam="baz" />
    
    // tốt
    <Foo
      superLongParam="bar"
      anotherSuperLongParam="baz"
    />
    
    // Nếu props phù hợp trong một dòng thì giữ nó trên cùng một dòng
    <Foo bar="bar" />
    
    // Component con được thụt lề bình thường
    <Foo
      superLongParam="bar"
      anotherSuperLongParam="baz"
    >
      <Quux />
    </Foo>
    9

    // tệ
    import reservationCard from './ReservationCard';
    
    // tốt
    import ReservationCard from './ReservationCard';
    
    // tệ
    const ReservationItem = <ReservationCard />;
    
    // tốt
    const reservationItem = <ReservationCard />;
    5

Phương thức

  • Sử dụng arrow function để bao đóng các biến cục bộ.

    // tệ
    import reservationCard from './ReservationCard';
    
    // tốt
    import ReservationCard from './ReservationCard';
    
    // tệ
    const ReservationItem = <ReservationCard />;
    
    // tốt
    const reservationItem = <ReservationCard />;
    6

  • Các hàm binding được gọi trong lúc render nên đặt ở trong hàm khởi tạo(constructor). eslint:

    // tệ
    <Foo bar='bar' />
    
    // tốt
    <Foo bar="bar" />
    
    // tệ
    <Foo style={{ left: "20px" }} />
    
    // tốt
    <Foo style={{ left: '20px' }} />
    0

    Tại sao? Vì nếu bind trong hàm render thì mỗi khi render, hàm đó lại được tạo mới một lần khiến cho hiệu suất xử lí giảm.

    // tệ
    import reservationCard from './ReservationCard';
    
    // tốt
    import ReservationCard from './ReservationCard';
    
    // tệ
    const ReservationItem = <ReservationCard />;
    
    // tốt
    const reservationItem = <ReservationCard />;
    7

  • Không nên dùng dấu "_" đặt trước tên các hàm của Component

    Lí do? Vì dấu gạch dước thi thoảng được dùng trong một số ngôn ngữ để biểu thị tính "private". Tuy nhiên, không giống các ngôn ngữ khác, trong JavaScript, mọi thứ đều là “public”. Cho dù bạn có cho dấu gạch dưới vào hay không nó vẫn là public, bất kể ý định của bạn. Hãy xem vấn đề #1024, và #490 để hiểu sâu hơn.

    // tệ
    import reservationCard from './ReservationCard';
    
    // tốt
    import ReservationCard from './ReservationCard';
    
    // tệ
    const ReservationItem = <ReservationCard />;
    
    // tốt
    const reservationItem = <ReservationCard />;
    8

  • Phải trả về một giá trị trong hàm

    // tệ
    <Foo bar='bar' />
    
    // tốt
    <Foo bar="bar" />
    
    // tệ
    <Foo style={{ left: "20px" }} />
    
    // tốt
    <Foo style={{ left: '20px' }} />
    1. eslint:
    // tệ
    <Foo bar='bar' />
    
    // tốt
    <Foo bar="bar" />
    
    // tệ
    <Foo style={{ left: "20px" }} />
    
    // tốt
    <Foo style={{ left: '20px' }} />
    2

    // tệ
    import reservationCard from './ReservationCard';
    
    // tốt
    import ReservationCard from './ReservationCard';
    
    // tệ
    const ReservationItem = <ReservationCard />;
    
    // tốt
    const reservationItem = <ReservationCard />;
    9

Cách sắp xếp hàm

  • Các hàm trong
    // tệ
    import Footer from './Footer/Footer';
    
    // tệ
    import Footer from './Footer/index';
    
    // tốt
    import Footer from './Footer';
    6 nên được viết theo thứ tự sau:
  1. Các phương thức tĩnh
    // tệ
    <Foo bar='bar' />
    
    // tốt
    <Foo bar="bar" />
    
    // tệ
    <Foo style={{ left: "20px" }} />
    
    // tốt
    <Foo style={{ left: '20px' }} />
    4 (không bắt buộc)
  2. // tệ
    <Foo bar='bar' />
    
    // tốt
    <Foo bar="bar" />
    
    // tệ
    <Foo style={{ left: "20px" }} />
    
    // tốt
    <Foo style={{ left: '20px' }} />
    5
  3. // tệ
    <Foo bar='bar' />
    
    // tốt
    <Foo bar="bar" />
    
    // tệ
    <Foo style={{ left: "20px" }} />
    
    // tốt
    <Foo style={{ left: '20px' }} />
    6
  4. // tệ
    <Foo bar='bar' />
    
    // tốt
    <Foo bar="bar" />
    
    // tệ
    <Foo style={{ left: "20px" }} />
    
    // tốt
    <Foo style={{ left: '20px' }} />
    7
  5. // tệ
    <Foo bar='bar' />
    
    // tốt
    <Foo bar="bar" />
    
    // tệ
    <Foo style={{ left: "20px" }} />
    
    // tốt
    <Foo style={{ left: '20px' }} />
    8
  6. // tệ
    <Foo bar='bar' />
    
    // tốt
    <Foo bar="bar" />
    
    // tệ
    <Foo style={{ left: "20px" }} />
    
    // tốt
    <Foo style={{ left: '20px' }} />
    9
  7. // tệ
    <Foo/>
    
    // rất tệ
    <Foo                 />
    
    // tệ
    <Foo
    />
    
    // tốt
    <Foo />
    0
  8. // tệ
    <Foo/>
    
    // rất tệ
    <Foo                 />
    
    // tệ
    <Foo
    />
    
    // tốt
    <Foo />
    1
  9. // tệ
    <Foo/>
    
    // rất tệ
    <Foo                 />
    
    // tệ
    <Foo
    />
    
    // tốt
    <Foo />
    2
  10. Hàm xử lí sự kiện như click hoặc submit
    // tệ
    <Foo/>
    
    // rất tệ
    <Foo                 />
    
    // tệ
    <Foo
    />
    
    // tốt
    <Foo />
    3 &
    // tệ
    <Foo/>
    
    // rất tệ
    <Foo                 />
    
    // tệ
    <Foo
    />
    
    // tốt
    <Foo />
    4
  11. Các hàm lấy dữ liệu cho hàm
    // tệ
    <Foo bar='bar' />
    
    // tốt
    <Foo bar="bar" />
    
    // tệ
    <Foo style={{ left: "20px" }} />
    
    // tốt
    <Foo style={{ left: '20px' }} />
    1 chẳng hạn như
    // tệ
    <Foo/>
    
    // rất tệ
    <Foo                 />
    
    // tệ
    <Foo
    />
    
    // tốt
    <Foo />
    6 hay
    // tệ
    <Foo/>
    
    // rất tệ
    <Foo                 />
    
    // tệ
    <Foo
    />
    
    // tốt
    <Foo />
    7
  12. Các hàm render khác như
    // tệ
    <Foo/>
    
    // rất tệ
    <Foo                 />
    
    // tệ
    <Foo
    />
    
    // tốt
    <Foo />
    8 hay
    // tệ
    <Foo/>
    
    // rất tệ
    <Foo                 />
    
    // tệ
    <Foo
    />
    
    // tốt
    <Foo />
    9
  13. // tệ
    <Foo bar='bar' />
    
    // tốt
    <Foo bar="bar" />
    
    // tệ
    <Foo style={{ left: "20px" }} />
    
    // tốt
    <Foo style={{ left: '20px' }} />
    1
  • Cách định nghĩa

    // tệ
    class Listing extends React.Component {
      render() {
        return <div>{this.props.hello}</div>;
      }
    }
    
    // tệ (dựa vào tên hàm để suy luận thì rất đau đầu)
    const Listing = ({ hello }) => (
      <div>{hello}</div>
    );
    
    // tốt
    function Listing({ hello }) {
      return <div>{hello}</div>;
    }
    01,
    // tệ
    class Listing extends React.Component {
      render() {
        return <div>{this.props.hello}</div>;
      }
    }
    
    // tệ (dựa vào tên hàm để suy luận thì rất đau đầu)
    const Listing = ({ hello }) => (
      <div>{hello}</div>
    );
    
    // tốt
    function Listing({ hello }) {
      return <div>{hello}</div>;
    }
    02,
    // tệ
    class Listing extends React.Component {
      render() {
        return <div>{this.props.hello}</div>;
      }
    }
    
    // tệ (dựa vào tên hàm để suy luận thì rất đau đầu)
    const Listing = ({ hello }) => (
      <div>{hello}</div>
    );
    
    // tốt
    function Listing({ hello }) {
      return <div>{hello}</div>;
    }
    03, ...

    // tệ
    import Footer from './Footer/Footer';
    
    // tệ
    import Footer from './Footer/index';
    
    // tốt
    import Footer from './Footer';
    0

  • Các hàm trong

    // tệ
    import Footer from './Footer/Footer';
    
    // tệ
    import Footer from './Footer/index';
    
    // tốt
    import Footer from './Footer';
    1 nên được viết theo thứ tự sau:
    // tệ
    class Listing extends React.Component {
      render() {
        return <div>{this.props.hello}</div>;
      }
    }
    
    // tệ (dựa vào tên hàm để suy luận thì rất đau đầu)
    const Listing = ({ hello }) => (
      <div>{hello}</div>
    );
    
    // tốt
    function Listing({ hello }) {
      return <div>{hello}</div>;
    }
    05react/sort-comp`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/sort-comp.md)

  1. // tệ
    export default function withFoo(WrappedComponent) {
      return function WithFoo(props) {
        return <WrappedComponent {...props} foo />;
      }
    }
    
    // tốt
    export default function withFoo(WrappedComponent) {
      function WithFoo(props) {
        return <WrappedComponent {...props} foo />;
      }
    
      const wrappedComponentName = WrappedComponent.displayName
        || WrappedComponent.name
        || 'Component';
    
      WithFoo.displayName = `withFoo(${wrappedComponentName})`;
      return WithFoo;
    }
    6
  2. // tệ
    class Listing extends React.Component {
      render() {
        return <div>{this.props.hello}</div>;
      }
    }
    
    // tệ (dựa vào tên hàm để suy luận thì rất đau đầu)
    const Listing = ({ hello }) => (
      <div>{hello}</div>
    );
    
    // tốt
    function Listing({ hello }) {
      return <div>{hello}</div>;
    }
    01
  3. // tệ
    class Listing extends React.Component {
      render() {
        return <div>{this.props.hello}</div>;
      }
    }
    
    // tệ (dựa vào tên hàm để suy luận thì rất đau đầu)
    const Listing = ({ hello }) => (
      <div>{hello}</div>
    );
    
    // tốt
    function Listing({ hello }) {
      return <div>{hello}</div>;
    }
    03
  4. // tệ
    class Listing extends React.Component {
      render() {
        return <div>{this.props.hello}</div>;
      }
    }
    
    // tệ (dựa vào tên hàm để suy luận thì rất đau đầu)
    const Listing = ({ hello }) => (
      <div>{hello}</div>
    );
    
    // tốt
    function Listing({ hello }) {
      return <div>{hello}</div>;
    }
    09
  5. // tệ
    class Listing extends React.Component {
      render() {
        return <div>{this.props.hello}</div>;
      }
    }
    
    // tệ (dựa vào tên hàm để suy luận thì rất đau đầu)
    const Listing = ({ hello }) => (
      <div>{hello}</div>
    );
    
    // tốt
    function Listing({ hello }) {
      return <div>{hello}</div>;
    }
    10
  6. // tệ
    class Listing extends React.Component {
      render() {
        return <div>{this.props.hello}</div>;
      }
    }
    
    // tệ (dựa vào tên hàm để suy luận thì rất đau đầu)
    const Listing = ({ hello }) => (
      <div>{hello}</div>
    );
    
    // tốt
    function Listing({ hello }) {
      return <div>{hello}</div>;
    }
    11
  7. // tệ
    class Listing extends React.Component {
      render() {
        return <div>{this.props.hello}</div>;
      }
    }
    
    // tệ (dựa vào tên hàm để suy luận thì rất đau đầu)
    const Listing = ({ hello }) => (
      <div>{hello}</div>
    );
    
    // tốt
    function Listing({ hello }) {
      return <div>{hello}</div>;
    }
    02
  8. // tệ
    class Listing extends React.Component {
      render() {
        return <div>{this.props.hello}</div>;
      }
    }
    
    // tệ (dựa vào tên hàm để suy luận thì rất đau đầu)
    const Listing = ({ hello }) => (
      <div>{hello}</div>
    );
    
    // tốt
    function Listing({ hello }) {
      return <div>{hello}</div>;
    }
    13
  9. // tệ
    class Listing extends React.Component {
      render() {
        return <div>{this.props.hello}</div>;
      }
    }
    
    // tệ (dựa vào tên hàm để suy luận thì rất đau đầu)
    const Listing = ({ hello }) => (
      <div>{hello}</div>
    );
    
    // tốt
    function Listing({ hello }) {
      return <div>{hello}</div>;
    }
    14
  10. // tệ
    <Foo bar='bar' />
    
    // tốt
    <Foo bar="bar" />
    
    // tệ
    <Foo style={{ left: "20px" }} />
    
    // tốt
    <Foo style={{ left: '20px' }} />
    6
  11. // tệ
    <Foo bar='bar' />
    
    // tốt
    <Foo bar="bar" />
    
    // tệ
    <Foo style={{ left: "20px" }} />
    
    // tốt
    <Foo style={{ left: '20px' }} />
    7
  12. // tệ
    <Foo bar='bar' />
    
    // tốt
    <Foo bar="bar" />
    
    // tệ
    <Foo style={{ left: "20px" }} />
    
    // tốt
    <Foo style={{ left: '20px' }} />
    8
  13. // tệ
    class Listing extends React.Component {
      render() {
        return <div>{this.props.hello}</div>;
      }
    }
    
    // tệ (dựa vào tên hàm để suy luận thì rất đau đầu)
    const Listing = ({ hello }) => (
      <div>{hello}</div>
    );
    
    // tốt
    function Listing({ hello }) {
      return <div>{hello}</div>;
    }
    18
  14. // tệ
    <Foo bar='bar' />
    
    // tốt
    <Foo bar="bar" />
    
    // tệ
    <Foo style={{ left: "20px" }} />
    
    // tốt
    <Foo style={{ left: '20px' }} />
    9
  15. // tệ
    <Foo/>
    
    // rất tệ
    <Foo                 />
    
    // tệ
    <Foo
    />
    
    // tốt
    <Foo />
    0
  16. // tệ
    <Foo/>
    
    // rất tệ
    <Foo                 />
    
    // tệ
    <Foo
    />
    
    // tốt
    <Foo />
    1
  17. // tệ
    <Foo/>
    
    // rất tệ
    <Foo                 />
    
    // tệ
    <Foo
    />
    
    // tốt
    <Foo />
    2
  18. Hàm xử lí sự kiện như
    // tệ
    <Foo/>
    
    // rất tệ
    <Foo                 />
    
    // tệ
    <Foo
    />
    
    // tốt
    <Foo />
    3 hay
    // tệ
    <Foo/>
    
    // rất tệ
    <Foo                 />
    
    // tệ
    <Foo
    />
    
    // tốt
    <Foo />
    4
  19. Các hàm lấy dữ liệu cho phương thức
    // tệ
    <Foo bar='bar' />
    
    // tốt
    <Foo bar="bar" />
    
    // tệ
    <Foo style={{ left: "20px" }} />
    
    // tốt
    <Foo style={{ left: '20px' }} />
    1 như
    // tệ
    <Foo/>
    
    // rất tệ
    <Foo                 />
    
    // tệ
    <Foo
    />
    
    // tốt
    <Foo />
    6 hay
    // tệ
    <Foo/>
    
    // rất tệ
    <Foo                 />
    
    // tệ
    <Foo
    />
    
    // tốt
    <Foo />
    7
  20. Các hàm render khác như
    // tệ
    <Foo/>
    
    // rất tệ
    <Foo                 />
    
    // tệ
    <Foo
    />
    
    // tốt
    <Foo />
    8 hay
    // tệ
    <Foo/>
    
    // rất tệ
    <Foo                 />
    
    // tệ
    <Foo
    />
    
    // tốt
    <Foo />
    9
  21. // tệ
    <Foo bar='bar' />
    
    // tốt
    <Foo bar="bar" />
    
    // tệ
    <Foo style={{ left: "20px" }} />
    
    // tốt
    <Foo style={{ left: '20px' }} />
    1
  • Không nên sử dụng
    // tệ
    import Footer from './Footer/Footer';
    
    // tệ
    import Footer from './Footer/index';
    
    // tốt
    import Footer from './Footer';
    2. eslint:
    // tệ
    class Listing extends React.Component {
      render() {
        return <div>{this.props.hello}</div>;
      }
    }
    
    // tệ (dựa vào tên hàm để suy luận thì rất đau đầu)
    const Listing = ({ hello }) => (
      <div>{hello}</div>
    );
    
    // tốt
    function Listing({ hello }) {
      return <div>{hello}</div>;
    }
    33

Tại sao? Vì

// tệ
import Footer from './Footer/Footer';

// tệ
import Footer from './Footer/index';

// tốt
import Footer from './Footer';
2 là một anti-pattern(mẫu nên tránh), không có sẵn khi dùng ES6 classes, và đang bị phản đối từ cộng đồng.