用更合理的方式書寫React和JSX
基本規則
- 每個文件只包含一個React組件;
- 但是無狀態, 或者 Pure 組件 允許一個文件包含多個組件。eslint: react/no-multi-comp.
- 始終使用 JSX 語法;
- 不要使用 React.createElement方法,除非初始化 app 的文件不是 JSX 格式。
Class vs React.createClass vs stateless
-
如果組件擁有內部的 state 或者 refs 的時,更推薦使用 class extends React.Component,除非你有一個非常好的理由要使用 mixin。 eslint: react/prefer-es6-class
// bad const Listing = React.createClass({ // ... render() { return <div>{this.state.hello}</div>; } }); // good class Listing extends React.Component { // ... render() { return <div>{this.state.hello}</div>; } }
如果沒有組件沒有內部 state 或者 refs,那么普通函數 (不要使用箭頭函數) 比類的寫法更好:
// bad class Listing extends React.Component { render() { return <div>{this.props.hello}</div>; } } // bad (因為箭頭函數沒有“name”屬性) const Listing = ({ hello }) => ( <div>{hello}</div> ); // good function Listing({ hello }) { return <div>{hello}</div>; }
命名
- 擴展名:React 組件使用.jsx擴展名;
- 文件名:文件名使用帕斯卡命名。 例如: ReservationCard.jsx。
-
引用命名:React 組件使用帕斯卡命名,引用實例采用駝峰命名。 eslint: react/jsx-pascal-case
// bad import reservationCard from './ReservationCard'; // good import ReservationCard from './ReservationCard'; // bad const ReservationItem = <ReservationCard />; // good const reservationItem = <ReservationCard />;
-
組件命名:組件名稱應該和文件名一致, 例如: ReservationCard.jsx 應該有一個ReservationCard的引用名稱。 但是, 如果是在目錄中的組件, 應該使用 index.jsx 作為文件名 並且使用文件夾名稱作為組件名:
// bad import Footer from './Footer/Footer'; // bad import Footer from './Footer/index'; // good import Footer from './Footer';
聲明
-
不要使用`displayName`屬性來命名組件,應該使用類的引用名稱。
// bad export default React.createClass({ displayName: 'ReservationCard', // stuff goes here }); // good export default class ReservationCard extends React.Component { }
對齊
-
為 JSX 語法使用下列的對其方式。eslint: react/jsx-closing-bracket-location
// bad <Foo superLongParam="bar" anotherSuperLongParam="baz" /> // good <Foo superLongParam="bar" anotherSuperLongParam="baz" />
// 如果組件的屬性可以放在一行就保持在當前一行中 <Foo bar="bar" /> // 多行屬性采用縮進 <Foo superLongParam="bar" anotherSuperLongParam="baz" > <Quux /> </Foo>
引號
-
JSX 的屬性都采用雙引號,其他的 JS 都使用單引號。eslint: jsx-quotes
為什么這樣做?JSX 屬性 不能包含轉義的引號, 所以當輸入"don't"這類的縮寫的時候用雙引號會更方便。
標准的 HTML 屬性通常也會使用雙引號,所以 JSX 屬性也會遵守這樣的約定。
// bad <Foo bar='bar' /> // good <Foo bar="bar" /> // bad <Foo style={{ left: "20px" }} /> // good <Foo style={{ left: '20px' }} />
空格
-
終始在自閉合標簽前面添加一個空格。
// bad <Foo/> // very bad <Foo /> // bad <Foo /> // good <Foo />
屬性
-
屬性名稱始終使用駝峰命名法。
// bad <Foo UserName="hello" phone_number={12345678} /> // good <Foo userName="hello" phoneNumber={12345678} />
-
當屬性值等於true的時候,省略該屬性的賦值。 eslint: react/jsx-boolean-value
// bad <Foo hidden={true} /> // good <Foo hidden />
括號
-
用括號包裹多行 JSX 標簽。 eslint: react/wrap-multilines
// bad render() { return <MyComponent className="long body" foo="bar"> <MyChild /> </MyComponent>; } // good render() { return ( <MyComponent className="long body" foo="bar"> <MyChild /> </MyComponent> ); } // good, when single line render() { const body = <div>hello</div>; return <MyComponent>{body}</MyComponent>; }
標簽
-
當標簽沒有子元素時,始終時候自閉合標簽。 eslint: react/self-closing-comp
// bad <Foo className="stuff"></Foo> // good <Foo className="stuff" />
-
如果控件有多行屬性,關閉標簽要另起一行。 eslint: react/jsx-closing-bracket-location
// bad <Foo bar="bar" baz="baz" /> // good <Foo bar="bar" baz="baz" />
方法
-
在 render 方法中事件的回調函數,應該在構造函數中進行bind綁定。 eslint: react/jsx-no-bind
為什么這樣做? 在 render 方法中的 bind 調用每次調用 render 的時候都會創建一個全新的函數。
// bad class extends React.Component { onClickDiv() { // do stuff } render() { return <div onClick={this.onClickDiv.bind(this)} /> } } // good class extends React.Component { constructor(props) { super(props); this.onClickDiv = this.onClickDiv.bind(this); } onClickDiv() { // do stuff } render() { return <div onClick={this.onClickDiv} /> } }
-
React 組件的內部方法命名不要使用下划線前綴。
// bad React.createClass({ _onClickSubmit() { // do stuff }, // other stuff }); // good class extends React.Component { onClickSubmit() { // do stuff } // other stuff }
排序
-
class extends React.Component的順序:
- static靜態方法
- constructor
- getChildContext
- componentWillMount
- componentDidMount
- componentWillReceiveProps
- shouldComponentUpdate
- componentWillUpdate
- componentDidUpdate
- componentWillUnmount
- 點擊回調或者事件回調 比如 onClickSubmit() 或者 onChangeDescription()
- render函數中的 getter 方法 比如 getSelectReason() 或者 getFooterContent()
- 可選的 render 方法 比如 renderNavigation() 或者 renderProfilePicture()
-
render
-
怎樣定義 propTypes, defaultProps, contextTypes等
import React, { PropTypes } from 'react'; const propTypes = { id: PropTypes.number.isRequired, url: PropTypes.string.isRequired, text: PropTypes.string, }; const defaultProps = { text: 'Hello World', }; 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;
-
React.createClass的排序:eslint: react/sort-comp
- displayName
- propTypes
- contextTypes
- childContextTypes
- mixins
- statics
- defaultProps
- getDefaultProps
- getInitialState
- getChildContext
- componentWillMount
- componentDidMount
- componentWillReceiveProps
- shouldComponentUpdate
- componentWillUpdate
- componentDidUpdate
- componentWillUnmount
- 點擊回調或者事件回調 比如 onClickSubmit() 或者 onChangeDescription()
- render函數中的 getter 方法 比如 getSelectReason() 或者 getFooterContent()
- 可選的 render 方法 比如 renderNavigation() 或者 renderProfilePicture()
- render
isMounted
-
不要使用 isMounted. eslint: react/no-is-mounted
為什么這樣做? isMounted是一種反模式,當使用 ES6 類風格聲明 React 組件時該屬性不可用,並且即將被官方棄用。
擴展
- 上訴的規則,你可用通過eslint的react插件來完成配置:eslint-plugin-react ,javascript/packages/eslint-config-airbnb at master · airbnb/javascript · GitHub
- Fork了一個官方的repo,把規范翻譯在這里,不僅有react的,還有兩個很火的翻譯版的ES5和ES6規則,強烈推薦沒有讀過的人讀一下,對你了解ES6也是非常好的途徑。GitHub - vikingmute/javascript: JavaScript Style Guide