摘要:本文介紹了,我們團隊寫組件的最佳實踐。這樣可以避免類似之類的錯誤避免使用函數(shù)表達式的方式來定義組件,如下這看起來非常酷,但是在這里,通過函數(shù)表達式定義的函數(shù)卻是匿名函數(shù)。匿名函數(shù)也可能會導致測試庫出問題。
本文為譯文,已獲得原作者允許,原文地址:http://scottdomes.com/blog/ou...
當我第一次開始寫 React 時,我發(fā)現(xiàn)多少個 React 教程,就有多少種寫 React 組件方法。雖然如今,框架已經(jīng)成熟,但是并沒有一個 “正確” 寫組件的方法。
在 MuseFind 的一年以來,我們的團隊寫了大量的 React 組件。我們精益求精,不斷完善寫 React 組件的方法。
本文介紹了,我們團隊寫 React 組件的最佳實踐。
我們希望,無論你是初學者,還是經(jīng)驗豐富的人,這篇文章都會對你有用的。
在開始介紹之前,先說幾個點:
我們團隊使用 ES6 和 ES7 的語法。
如果不清楚表現(xiàn)組件(presentational components)和容器組件(container components)之間的區(qū)別,我們建議先閱讀 這篇文章。
如果有任何建議,問題或反饋意見,請在評論中通知我們。
基于類的組件基于類的組件(Class based components)是包含狀態(tài)和方法的。
我們應該盡可能地使用基于函數(shù)的組件(Functional Components
)來代替它們。但是,現(xiàn)在讓我們先來講講怎么寫基于類的組件。
讓我們逐行地構(gòu)建我們的組件。
引入 CSSimport React, { Component } from "react" import { observer } from "mobx-react" import ExpandableForm from "./ExpandableForm" import "./styles/ProfileContainer.css"
我認為最理想的 CSS 應該是 CSS in JavaScript。但是,這仍然是一個新的想法,還沒有一個成熟的解決方案出現(xiàn)。
所以,現(xiàn)在我們還是使用將 CSS 文件引入到每個 React 組件中的方法。
我們團隊會先引入依賴文件(node_modules 中的文件),然后空一行,再引入本地文件。
初始化狀態(tài)import React, { Component } from "react" import { observer } from "mobx-react" import ExpandableForm from "./ExpandableForm" import "./styles/ProfileContainer.css" export default class ProfileContainer extends Component { state = { expanded: false }
可以使用在 constructor 中初始化狀態(tài)的老方法。
也可以使用 ES7 這種簡單的初始化狀態(tài)的新方法。
更多,請閱讀 這里。
import React, { Component } from "react" import { observer } from "mobx-react" import { string, object } from "prop-types" import ExpandableForm from "./ExpandableForm" import "./styles/ProfileContainer.css" export default class ProfileContainer extends Component { state = { expanded: false } static propTypes = { model: object.isRequired, title: string } static defaultProps = { model: { id: 0 }, title: "Your Name" }
propTypes 和 defaultProps 是靜態(tài)屬性(static properties),在組件代碼中,最好把它們寫在組件靠前的位置。當其他開發(fā)人員查看這個組件的代碼時,應該立即看到 propTypes 和 defaultProps,因為它們就好像這個組件的文檔一樣。(譯注:關于組件書寫的順序,參考 這篇文章)
如果使用 React 15.3.0 或更高版本,請使用 prop-types 代替 React.PropTypes。使用 prop-types 時,應當將其解構(gòu)。
所有組件都應該有 propTypes。
Methodsimport React, { Component } from "react" import { observer } from "mobx-react" import { string, object } from "prop-types" import ExpandableForm from "./ExpandableForm" import "./styles/ProfileContainer.css" export default class ProfileContainer extends Component { state = { expanded: false } static propTypes = { model: object.isRequired, title: string } static defaultProps = { model: { id: 0 }, title: "Your Name" } handleSubmit = (e) => { e.preventDefault() this.props.model.save() } handleNameChange = (e) => { this.props.model.changeName(e.target.value) } handleExpand = (e) => { e.preventDefault() this.setState({ expanded: !this.state.expanded }) }
使用基于類的組件時,當你將方法傳遞給組件時,你必須保證方法在調(diào)用時具有正確的上下文 this。常見的方法是,通過將 this.handleSubmit.bind(this) 傳遞給子組件來實現(xiàn)。
我們認為,上述方法更簡單,更直接。通過 ES6 箭頭功能自動 bind 正確的上下文。
給 setState 傳遞一個函數(shù)在上面的例子中,我們這樣做:
this.setState({ expanded: !this.state.expanded })
因為 setState 它實際上是異步的。
由于性能原因,所以 React 會批量的更新狀態(tài),因此調(diào)用 setState 后狀態(tài)可能不會立即更改。
這意味著在調(diào)用 setState 時,不應該依賴當前狀態(tài),因為你不能確定該狀態(tài)是什么!
解決方案是:給 setState 傳遞函數(shù),而不是一個普通對象。函數(shù)的第一個參數(shù)是前一個狀態(tài)。
this.setState(prevState => ({ expanded: !prevState.expanded }))解構(gòu) Props
import React, { Component } from "react" import { observer } from "mobx-react" import { string, object } from "prop-types" import ExpandableForm from "./ExpandableForm" import "./styles/ProfileContainer.css" export default class ProfileContainer extends Component { state = { expanded: false } static propTypes = { model: object.isRequired, title: string } static defaultProps = { model: { id: 0 }, title: "Your Name" } handleSubmit = (e) => { e.preventDefault() this.props.model.save() } handleNameChange = (e) => { this.props.model.changeName(e.target.value) } handleExpand = (e) => { e.preventDefault() this.setState(prevState => ({ expanded: !prevState.expanded })) } render() { const { model, title } = this.props return () } } {title}
如上,當組件具有多個 props 值時,每個 prop 應當多帶帶占據(jù)一行。
裝飾器@observer export default class ProfileContainer extends Component {
如果使用 mobx,那么應當是用裝飾器(decorators)。其本質(zhì)是將裝飾器的組件傳遞到一個函數(shù)。
使用裝飾器一種更加靈活和更加可讀的方式。
我們團隊在使用 mobx 和我們自己的 mobx-models 庫時,使用了大量的裝飾器。
如果您不想使用裝飾器,也可以按照下面的方式做:
class ProfileContainer extends Component { // Component code } export default observer(ProfileContainer)閉包
避免傳遞一個新閉包(Closures)給子組件,像下面這樣:
{ model.name = e.target.value }} // ^ 上面是錯誤的. 使用下面的方法: onChange={this.handleChange} placeholder="Your Name"/>
為什么呢?因為每次父組件 render 時,都會創(chuàng)建一個新的函數(shù)(譯注:通過 (e) => { model.name = e.target.value } 創(chuàng)建的新的函數(shù)也叫 閉包)。
如果將這個新函數(shù)傳給一個 React 組件,無論這個組件的其他 props 有沒有真正的改變,都就會導致它重新渲染。
調(diào)和(Reconciliation)是 React 中最耗費性能的一部分。因此,要避免傳遞新閉包的寫法,不要讓調(diào)和更加消耗性能!另外,傳遞類的方法的之中形式更容易閱讀,調(diào)試和更改。
下面是我們整個組件:
import React, { Component } from "react" import { observer } from "mobx-react" import { string, object } from "prop-types" // Separate local imports from dependencies import ExpandableForm from "./ExpandableForm" import "./styles/ProfileContainer.css" // Use decorators if needed @observer export default class ProfileContainer extends Component { state = { expanded: false } // Initialize state here (ES7) or in a constructor method (ES6) // Declare propTypes as static properties as early as possible static propTypes = { model: object.isRequired, title: string } // Default props below propTypes static defaultProps = { model: { id: 0 }, title: "Your Name" } // Use fat arrow functions for methods to preserve context (this will thus be the component instance) handleSubmit = (e) => { e.preventDefault() this.props.model.save() } handleNameChange = (e) => { this.props.model.name = e.target.value } handleExpand = (e) => { e.preventDefault() this.setState(prevState => ({ expanded: !prevState.expanded })) } render() { // Destructure props for readability const { model, title } = this.props return (基于函數(shù)的組件// Newline props if there are more than two ) } }{title}
{ model.name = e.target.value }} // Avoid creating new closures in the render method- use methods like below onChange={this.handleNameChange} placeholder="Your Name"/>
基于函數(shù)的組件(Functional Components)是沒有狀態(tài)和方法的。它們是純粹的、易讀的。盡可能的使用它們。
propTypesimport React from "react" import { observer } from "mobx-react" import { func, bool } from "prop-types" import "./styles/Form.css" ExpandableForm.propTypes = { onSubmit: func.isRequired, expanded: bool } // Component declaration
在聲明組件之前,給組件定義 propTypes,因為這樣它們可以立即被看見。
我們可以這樣做,因為 JavaScript 有函數(shù)提升(function hoisting)。
import React from "react" import { observer } from "mobx-react" import { func, bool } from "prop-types" import "./styles/Form.css" ExpandableForm.propTypes = { onSubmit: func.isRequired, expanded: bool, onExpand: func.isRequired } function ExpandableForm(props) { const formStyle = props.expanded ? {height: "auto"} : {height: 0} return () }
我們的組件是一個函數(shù),函數(shù)的參數(shù)就是組件的 props。我們可以使用解構(gòu)參數(shù)的方式:
import React from "react" import { observer } from "mobx-react" import { func, bool } from "prop-types" import "./styles/Form.css" ExpandableForm.propTypes = { onSubmit: func.isRequired, expanded: bool, onExpand: func.isRequired } function ExpandableForm({ onExpand, expanded = false, children, onSubmit }) { const formStyle = expanded ? {height: "auto"} : {height: 0} return () }
注意,我們還可以使用默認參數(shù)作為 defaultProps,這種方式可讀性更強。
如果 expanded 未定義,則將其設置為false。(這樣可以避免類似 ‘Cannot read
避免使用函數(shù)表達式的方式來定義組件,如下:
const ExpandableForm = ({ onExpand, expanded, children }) => {
這看起來非常酷,但是在這里,通過函數(shù)表達式定義的函數(shù)卻是匿名函數(shù)。
如果 Bable 沒有做相關的命名配置,那么報錯時,錯誤堆棧中不會告訴具體是哪個組件出錯了,只會顯示 <
匿名函數(shù)也可能會導致 React 測試庫 Jest 出問題。由于這些潛在的隱患,我們推薦使用函數(shù)聲明,而不是函數(shù)表達式。
包裹函數(shù)因為基于函數(shù)的組件不能使用修飾器,所以你應當將基于函數(shù)的組件當做參數(shù),傳給修飾器對應的函數(shù):
import React from "react" import { observer } from "mobx-react" import { func, bool } from "prop-types" import "./styles/Form.css" ExpandableForm.propTypes = { onSubmit: func.isRequired, expanded: bool, onExpand: func.isRequired } function ExpandableForm({ onExpand, expanded = false, children, onSubmit }) { const formStyle = expanded ? {height: "auto"} : {height: 0} return () } export default observer(ExpandableForm)
全部的代碼如下:
import React from "react" import { observer } from "mobx-react" import { func, bool } from "prop-types" // Separate local imports from dependencies import "./styles/Form.css" // Declare propTypes here, before the component (taking advantage of JS function hoisting) // You want these to be as visible as possible ExpandableForm.propTypes = { onSubmit: func.isRequired, expanded: bool, onExpand: func.isRequired } // Destructure props like so, and use default arguments as a way of setting defaultProps function ExpandableForm({ onExpand, expanded = false, children, onSubmit }) { const formStyle = expanded ? { height: "auto" } : { height: 0 } return () } // Wrap the component instead of decorating it export default observer(ExpandableForm) JSX 中的條件表達式
很可能你會做很多條件渲染。這是你想避免的:
不,三目嵌套不是一個好主意。
有一些庫解決了這個問題(JSX-Control Statementments),但是為了引入另一個依賴庫,我們使用復雜的條件表達式,解決了這個問題:
使用大括號包裹一個立即執(zhí)行函數(shù)(IIFE),然后把你的 if 語句放在里面,返回你想要渲染的任何東西。
請注意,像這樣的 IIFE 可能會導致一些性能消耗,但在大多數(shù)情況下,可讀性更加重要。
更新:許多評論者建議將此邏輯提取到子組件,由這些子組件返回的不同 button。這是對的,盡可能地拆分組件。
另外,當你有布爾判斷渲染元素時,不應該這樣做:
{ isTrue ?True!
:}
應該使用短路運算:
{ isTrue &&True!
}
文章版權歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/84884.html
摘要:本文針對技術棧,總結(jié)了一些最佳實踐,對編寫高質(zhì)量的代碼有一定的參考作用。二最佳實踐說明多用如果組件是純展示型的,不需要維護和生命周期,則優(yōu)先使用。理解并遵循這些最佳實踐,寫出來的代碼質(zhì)量會有一定的保證。 歡迎關注我的公眾號睿Talk,獲取我最新的文章:showImg(https://segmentfault.com/img/bVbmYjo); 一、前言 在日常開發(fā)和 Code Revi...
摘要:通過聲明式編程模型定義組件,是最強大的核心功能。無論是的瀏覽器書簽,還是的導航功能,只要是可以使用的地方,就可以使用。二級路由使用渲染組件屬性狀態(tài)請選擇一個主題。也許是最佳小實踐地址,覺得有幫助的話,請點擊一下,嘿嘿 小前言 這是一個小小的有關react的小例子,希望通過一個小例子,可以讓新手更好的了解到react、react-router4.0、redux的集中使用方法。 這是基...
摘要:引入初始化使用句法定義初始化和和的聲明應該置頂便于其他開發(fā)者閱讀。在版本,推薦使用這個包替代。組件內(nèi)的方法使用在方法中箭頭函數(shù)來替代是異步的。高階組件完整代碼在組件前聲明解構(gòu)通過函數(shù)入?yún)⒛J值的方式設定 原文:Our Best Practices for Writing React Components . 這里意譯。有些點在之前的文章里提到過:#2譯文地址:https://githu...
摘要:譯者按最近依舊如火如荼相信大家都躍躍欲試我們團隊也開始在領域有所嘗試年應該是逐漸走向成熟的一年讓我們一起來看看國外的開發(fā)者們都總結(jié)了哪些最佳實踐年在全世界都有很多關于新的更新和開發(fā)者大會的討論關于去年的重要事件請參考那么年最有趣的問題來了我 譯者按:最近React(web/native)依舊如火如荼,相信大家都躍躍欲試,我們團隊也開始在React領域有所嘗試. 2016年應該是Reac...
閱讀 2985·2021-10-12 10:17
閱讀 1589·2021-09-01 11:38
閱讀 1081·2019-08-30 15:44
閱讀 3479·2019-08-26 18:36
閱讀 507·2019-08-26 13:25
閱讀 1884·2019-08-26 10:29
閱讀 2835·2019-08-23 15:58
閱讀 759·2019-08-23 12:59