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: react/no-multi-comp
.
Luôn luôn sử dụng cú pháp JSX.
Không sử dụng React.createElement
chung với cú pháp JSX.
Nếu Component có state hoặc refs, nên sử dụng class extends React.Component
thay vì React.createClass
. eslint: react/prefer-es6-class
react/prefer-stateless-function
// tệconst Listing = React.createClass({// ...render() {return <div>{this.state.hello}</div>;}});// tốtclass 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ốtfunction Listing({ hello }) {return <div>{hello}</div>;}
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).
Phần mở rộng(extensions): Sử dụng phần mở rộng .jsx
cho React Components.
Tên file: Sử dụng chuẩn PascalCase cho tên file. Ví dụ: ReservationCard.jsx
.
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: react/jsx-pascal-case
// tệimport reservationCard from './ReservationCard';// tốtimport ReservationCard from './ReservationCard';// tệconst ReservationItem = <ReservationCard />;// tốtconst reservationItem = <ReservationCard />;
Đặt tên Component: Sử dụng tên file trùng với tên component. Ví dụ: ReservationCard.jsx
nên có tên tham chiếu là ReservationCard
. Tuy nhiên, đối với các component gốc của một thư mục, hãy sử dụng index.jsx
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ốtimport 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ư displayName
(tên hiển thị) trên component đuợc tạo ra. Ví dụ component bậc cao withFoo()
, khi truyền một component Bar
sẽ tạo ra một component với displayName
của withFoo(Bar)
.
Tại sao? Vì
displayName
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ốtexport 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ư
style
vàclassName
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" />
Không nên sử dụng displayName
để đặ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ốtexport default class ReservationCard extends React.Component {}
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<FoosuperLongParam="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<FoosuperLongParam="bar"anotherSuperLongParam="baz"><Quux /></Foo>
Luôn luôn sử dụng dấu ngoặc kép ("
) cho các thuộc tính JSX, nhưng dấu nháy đơn ('
) 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' }} />
Luôn luôn có duy nhất một kí tự space(khoảng trắng) trong thẻ tự đóng. eslint: no-multi-spaces
, react/jsx-tag-spacing
// 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: react/jsx-curly-spacing
// tệ<Foo bar={ baz } />// tốt<Foo bar={baz} />
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ệ<FooUserName="hello"phone_number={12345678}/>// tốt<FoouserName="hello"phoneNumber={12345678}/>
Bỏ giá trị của prop khi nó thực sự rõ ràng là true
. eslint: react/jsx-boolean-value
// tệ<Foohidden={true}/>// tốt<Foohidden/>// tốt<Foo hidden />
Luôn luôn sử dụng prop alt
trong thẻ <img>
. Nếu giá trị của thẻ là NULL , alt
có thể là một chuỗi rỗng hoặc <img>
phải có thuộc tính role="presentation"
. eslint: jsx-a11y/alt-text
// tệ<img src="hello.jpg" />// tốt<img src="hello.jpg" alt="Me waving hello" />// tốt<img src="hello.jpg" alt="" />// tốt<img src="hello.jpg" role="presentation" />
Không dùng các từ "image", "photo", hoặc "picture" trong <img>
alt
props. eslint: jsx-a11y/img-redundant-alt
Tại sao? Screenreaders đã tự hiểu
img
elements là image(ảnh), vì vậy không cần khai báo thông tin này trong alt
// tệ<img src="hello.jpg" alt="Picture of me waving hello" />// tốt<img src="hello.jpg" alt="Me waving hello" />
Chỉ sử dụng ARIA roles. eslint: jsx-a11y/aria-role
hợp lệ, và không trừu tượng. jsx-a11y/aria-role
// tệ - không phải ARIA roles<div role="datepicker" />//tệ- ARIA roles trừu tượng<div role="range" />// tốt<div role="button" />
Không dùng accessKey
trong các elements. eslint: jsx-a11y/no-access-key
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ệ<div accessKey="h" />// tốt<div />
Tránh dùng chỉ số của mảng(index) cho thuộc tính key
, nên sử dụng một unique ID(định danh duy nhất). (why?)
// tệ{todos.map((todo, index) =><Todo{...todo}key={index}/>)}// tốt{todos.map(todo => (<Todo{...todo}key={todo.id}/>))}
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ệfunction SFC({ foo, bar, children }) {return <div>{foo}{bar}{children}</div>;}SFC.propTypes = {foo: PropTypes.number.isRequired,bar: PropTypes.string,children: PropTypes.node,};// tốtfunction SFC({ foo, bar, children }) {return <div>{foo}{bar}{children}</div>;}SFC.propTypes = {foo: PropTypes.number.isRequired,bar: PropTypes.string,children: PropTypes.node,};SFC.defaultProps = {bar: '',children: null,};
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
function HOC(WrappedComponent) {return class Proxy extends React.Component {Proxy.propTypes = {text: PropTypes.string,isLoading: PropTypes.bool};render() {return <WrappedComponent {...this.props} />}}}
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.
export default function Foo {const props = {text: '',isPublished: false}return (<div {...props} />);}
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ốtrender() {const { irrelevantProp, ...relevantProps } = this.props;return <WrappedComponent {...relevantProps} />}// tệrender() {const { irrelevantProp, ...relevantProps } = this.props;return <WrappedComponent {...this.props} />}
Luôn sử dụng hàm gọi lại(callback) cho khai báo ref. eslint: react/no-string-refs
// tệ<Fooref="myRef"/>// tốt<Fooref={(ref) => { this.myRef = ref; }}/>
Đóng gói các thẻ JSX trong ngoặc đơn khi chúng kéo dài nhiều dòng. eslint: react/jsx-wrap-multilines
// tệrender() {return <MyComponent variant="long body" foo="bar"><MyChild /></MyComponent>;}// Tốtrender() {return (<MyComponent variant="long body" foo="bar"><MyChild /></MyComponent>);}// Tốt, Khi chỉ có 1 dòngrender() {const body = <div>hello</div>;return <MyComponent>{body}</MyComponent>;}
Luôn luôn tự đóng các thẻ(tags) không có con. eslint: react/self-closing-comp
// tệ<Foo variant="stuff"></Foo>// tốt<Foo variant="stuff" />
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: react/jsx-closing-bracket-location
// tệ<Foobar="bar"baz="baz" />// tốt<Foobar="bar"baz="baz"/>
Sử dụng arrow function để bao đóng các biến cục bộ.
function ItemList(props) {return (<ul>{props.items.map((item, index) => (<Itemkey={item.key}onClick={() => làmGìĐó(item.name, index)}/>))}</ul>);}
Các hàm binding được gọi trong lúc render nên đặt ở trong hàm khởi tạo(constructor). eslint: react/jsx-no-bind
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ệclass MyComponent extends React.Component {onClickDiv() {// làm việc gì đó}render() {return <div onClick={this.onClickDiv.bind(this)} />;}}// tốtclass MyComponent extends React.Component {constructor(props) {super(props);this.onClickDiv = this.onClickDiv.bind(this);}onClickDiv() {// Làm gì đó vui vẻ}render() {return <div onClick={this.onClickDiv} />;}}
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ệReact.createClass({_onClickSubmit() {// làm việc gì đó},// làm việc gì đó});// tốtclass extends React.Component {onClickSubmit() {// làm việc gì đó}// làm việc gì đó}
Phải trả về một giá trị trong hàm render
. eslint: react/require-render-return
// tệrender() {(<div />);}// tốtrender() {return (<div />);}
Các hàm trong class extends React.Component
nên được viết theo thứ tự sau:
Các phương thức tĩnh static
(không bắt buộc)
constructor
getChildContext
componentWillMount
componentDidMount
shouldComponentUpdate
componentWillUpdate
componentDidUpdate
componentWillUnmount
Hàm xử lí sự kiện như click hoặc submit onClickSubmit()
& onChangeDescription()
Các hàm lấy dữ liệu cho hàm render
chẳng hạn như getSelectReason()
hay getFooterContent()
Các hàm render khác như renderNavigation()
hay renderProfilePicture()
render
Cách định nghĩa propTypes
, defaultProps
, contextTypes
, ...
import React from 'react';import PropTypes from 'prop-types';const propTypes = {id: PropTypes.number.isRequired, // Nếu id không đúng kiểu number, màn hình console sẽ hiện ra cảnh báourl: PropTypes.string.isRequired, // Nếu url không đúng kiểu string, màn hình console sẽ hiện ra cảnh báotext: PropTypes.string, // Nếu text không đúng kiểu string, màn hình console sẽ hiện ra cảnh báo};const defaultProps = {text: 'Hello World', // Gán giá trị mặc định cho text};class Link extends React.Component {static methodsAreOk() {return true;}render() {return <a href={this.props.url} data-id={this.props.id}>{this.props.text}</a>;}}Link.propTypes = propTypes;Link.defaultProps = defaultProps;export default Link;
Các hàm trong React.createClass
nên được viết theo thứ tự sau: eslint: [
react/sort-comp`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/sort-comp.md)
displayName
propTypes
contextTypes
childContextTypes
mixins
statics
defaultProps
getDefaultProps
getInitialState
getChildContext
componentWillMount
componentDidMount
componentWillReceiveProps
shouldComponentUpdate
componentWillUpdate
componentDidUpdate
componentWillUnmount
Hàm xử lí sự kiện như onClickSubmit()
hay onChangeDescription()
Các hàm lấy dữ liệu cho phương thức render
nhưgetSelectReason()
hay getFooterContent()
Các hàm render khác như renderNavigation()
hay renderProfilePicture()
render
Không nên sử dụng isMounted
. eslint: react/no-is-mounted
Tại sao? Vì
isMounted
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.
( Nguồn : Airbnb React/JSX Style Guide )