編碼規範-----react
- 原則上每個檔案只寫一個元件, 多個無狀態元件可以放在單個檔案中. eslint:
react/no-multi-comp
. - 推薦使用 JSX 語法編寫 React 元件, 而不是
React.createElement
Class vs React.createClass vs stateless
// badconstListing=React.createClass({// ... render(){return<div>{this.state.hello}</div>;}});// goodclassListingextendsReact.Component
如果你的元件沒有狀態或是沒有引用
refs
, 推薦使用普通函式(非箭頭函式)而不是類:// badclassListingextendsReact.Component{ render(){return<div>{this.props.hello}</div>;}}// bad (relying on function name inference is discouraged)constListing=({ hello })=>(<div>
因為 Mixins 會增加隱式依賴, 導致命名衝突, 並且會增加複雜度.大多數情況下, 可以更好的方法代替 Mixins, 如:元件化, 高階元件, 工具元件等.
副檔名: React 元件使用
.jsx
副檔名.
- 檔名: 檔名使用帕斯卡命名. 如,ReservationCard.jsx
.
- 引用命名: React元件名使用帕斯卡命名, 例項使用駱駝式命名. eslint:react/jsx-pascal-case
// badimport reservationCard from'./ReservationCard';// goodimportReservationCardfrom'./ReservationCard';// badconstReservationItem=<ReservationCard/>;// goodconst reservationItem =<ReservationCard/>;
元件命名: 元件名與當前檔名一致. 如
ReservationCard.jsx
應該包含名為ReservationCard
的元件. 如果整個目錄是一個元件, 使用index.js
作為入口檔案, 然後直接使用index.js
或者目錄名作為元件的名稱:// badimportFooterfrom'./Footer/Footer';// badimportFooterfrom'./Footer/index';// goodimportFooterfrom'./Footer';
高階元件命名: 生成一個新的元件時, 其中的元件名
displayName
應該為高階元件名和傳入元件名的組合. 例如, 高階元件withFoo()
, 當傳入一個Bar
元件的時候, 生成的元件名displayName
應該為withFoo(Bar)
.因為一個元件的
displayName
可能在除錯工具或錯誤資訊中使用到. 清晰合理的命名, 能幫助我們更好的理解元件執行過程, 更好的 Debug.// badexportdefaultfunction withFoo(WrappedComponent){returnfunctionWithFoo(props){return<WrappedComponent{...props} foo />;}}// goodexportdefaultfunction withFoo(WrappedComponent){functionWithFoo(props){return<WrappedComponent{...props} foo />;}const wrappedComponentName =WrappedComponent.displayName ||WrappedComponent.name ||'Component';WithFoo.displayName =`withFoo(${wrappedComponentName})`;returnWithFoo;}
屬性命名: 避免使用 DOM 相關的屬性來用命名自定義屬性
因為對於
style
和className
這樣的屬性名, 我們都會預設它們代表一些特殊的含義.// bad<MyComponent style="fancy"/>// good<MyComponent variant="fancy"/>
不要使用
displayName
來命名 React 元件, 而是使用引用來命名元件, 如 class 名稱.// badexportdefaultReact.createClass({ displayName:'ReservationCard',// stuff goes here});// goodexportdefaultclassReservationCardextendsReact.Component{}
// bad<Foo superLongParam="bar" anotherSuperLongParam="baz"/>// good, 有多行屬性的話, 新建一行關閉標籤<Foo superLongParam="bar" anotherSuperLongParam="baz"/>// 若能在一行中顯示, 直接寫成一行<Foo bar="bar"/>// 子元素按照常規方式縮排<Foo superLongParam="bar" anotherSuperLongParam="baz"><Quux/></Foo>
對於 JSX 屬性值總是使用雙引號(
"
), 其他均使用單引號('
). eslint:jsx-quotes
因為 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 bar={ baz }/>// good<Foo bar={baz}/>
JSX 屬性名使用駱駝式風格
camelCase
.// bad<FooUserName="hello" phone_number={12345678}/>// good<Foo userName="hello" phoneNumber={12345678}/>
// bad<Foo hidden={true}/>// good<Foo hidden />
<img>
標籤總是新增alt
屬性. 如果圖片以陳述方式顯示,alt
可為空, 或者<img>
要包含role="presentation"
. eslint:jsx-a11y/alt-text
// bad<img src="hello.jpg"/>// good<img src="hello.jpg" alt="Me waving hello"/>// good<img src="hello.jpg" alt=""/>// good<img src="hello.jpg" role="presentation"/>
不要在
alt
值裡使用如 "image", "photo", or "picture" 包括圖片含義這樣的詞, 中文同理. eslint:jsx-a11y/img-redundant-alt
因為螢幕助讀器已經把
img
標籤標註為圖片了, 所以沒有必要再在alt
裡重複說明.// bad<img src="hello.jpg" alt="Picture of me waving hello"/>// good<img src="hello.jpg" alt="Me waving hello"/>
// bad - not an ARIA role<div role="datepicker"/>// bad - abstract ARIA role<div role="range"/>// good<div role="button"/>
因為螢幕助讀器在鍵盤快捷鍵與鍵盤命令時造成的不統一性會導致閱讀性更加複雜.
// bad<div accessKey="h"/>// good<div />
// bad{todos.map((todo, index)=><Todo{...todo} key={index}/>)}// good{todos.map(todo =>(<Todo{...todo} key={todo.id}/>))}
對於所有非必填寫屬性, 總是手動去定義
defaultProps
屬性.因為 propTypes 可以作為元件的文件說明, 而宣告 defaultProps 使閱讀程式碼的人不需要去假設預設值. 另外, 顯式的宣告預設屬性可以讓你的元件跳過屬性型別的檢查.
// badfunction SFC({ foo, bar, children }){return<div>{foo}{bar}{children}</div>;} SFC.propTypes ={ foo:PropTypes.number.isRequired, bar:PropTypes.string, children:PropTypes.node,};// goodfunction 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,};
// bad<Fooref="myRef"/>// good<Fooref={(ref)=>{this.myRef =ref;}}/>
// bad render(){return<MyComponent className="long body" foo="bar"><MyChild/></MyComponent>; } // good render(){return(<MyComponent className="long body" foo="bar"><MyChild/></MyComponent> ); } // good,單行可以不需要 render(){const body =<div>hello</div>; return <MyComponent>{body}</MyComponent>;}
// bad<Foo className="stuff"></Foo>// good<Foo className="stuff"/>
// bad<Foo bar="bar" baz="baz"/>// good<Foo bar="bar" baz="baz"/>
使用箭頭函式來獲取本地變數.
functionItemList(props){return(<ul>{props.items.map((item, index)=>(<Item key={item.key} onClick={()=> doSomethingWith(item.name, index)}/>))}</ul>);}
當在
render()
裡使用事件處理方法時, 提前在建構函式裡把this
繫結上去. eslint:react/jsx-no-bind
因為在每次
render
過程中, 再呼叫bind
都會新建一個新的函式, 浪費資源.// badclassextendsReact.Component{ onClickDiv(){// do stuff} render(){return<div onClick={this.onClickDiv.bind(this)}/>;}}// goodclassextendsReact.Component{ constructor(props){super(props);this.onClickDiv =this.onClickDiv.bind(this);} onClickDiv(){// do stuff