编写 React 组件的最佳实践_前端开发者_网站前端开发

前端开发者_网站前端开发丨JavaScript
https://www.rokub.com
编写 React 组件的最佳实践在我第一次编写 React 代码的时候, 我见发现许多不同的方法可以用来编写组件, 不同教程教授的内容也大不相同。
尽管从那时候起框架已经相当成熟, 但并没有一种固定的“ 正确” 方式指导。
在 MuseFind 工作的一年里, 我们的团队编写了许多 React 组件, 后期我们对方法进行了优化直到满意为止。
本指南描述了我们推荐的最佳实践, 不管你是一名初学者还是有经验的老手, 希望它能对你有所帮助。
在我们开始之前, 有几个地方要注意一下: 我们使用的是 ES6 和 ES7 的语法。
如果你对于现实和容器组件两者之间的区别不甚明了, 建议首先阅读一下这个。
如果有任何建议、 疑问或者感想, 请通过评论来让我们知晓。
基于类的组件基于类的组件具有丰富状态而且可以含有方法。 我们要尽可能有节制地去使用, 因为它们也有特定的适用场合。
让我们使用一行一行的代码逐步地将我们的组件构建起来吧。
引入 CSSimport React, {
Component
}
from ‘react’
import {
observer
} from ‘mobx-react’
import ExpandableForm from ‘./ExpandableForm’
import ‘./styles/ProfileContainer.css
1 2 3 4 5
import React, {
Component
} from ‘react’
import {
observer
} from ‘mobx-react’
import ExpandableForm from ‘./ExpandableForm’
import ‘./styles/ProfileContainer.css
我喜欢在 JavaScript 中操作 css, 这在理论上这样做是可行的。
不过它仍然是一种新的创意, 还没出现切实可行的解决方案。
不过在此之前, 我们可以先为每一个组件引入一个 css 文件。
我们也通过另写一行将依赖引入从本地引入独立了出来。
状态初始化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
}
12345678
importReact,{
Component
}from’react’
import{
observer
}from’mobx-react’
importExpandableFormfrom’./ExpandableForm’
import’./styles/ProfileContainer.css
exportdefaultclassProfileContainerextendsComponent{
state={
expanded: false
}
如果你使用的是ES6而不是ES7,就在构造器中进行状态初始化。
否则, 就使用这里的ES7方式。
更多内容可以在这里找到。
我们也要确保会默认将类导出。
propTypes和defaultPropsimportReact,{
Component
}
from’react’
import{
observer
}from’mobx-react’
importExpandableFormfrom’./ExpandableForm’
import’./styles/ProfileContainer.css
exportdefaultclassProfileContainerextendsComponent{
state={
expanded: false
}
staticpropTypes={
model: React.PropTypes.object.isRequired,
title: React.PropTypes.string
}
staticdefaultProps={
model: {
id: 0
},
title: ‘Your Name’
}
123456789101112131415161718
importReact,{
Component
}from’react’
import{
observer
}from’mobx-react’
importExpandableFormfrom’./ExpandableForm’
import’./styles/ProfileContainer.css’
exportdefaultclassProfileContainerextendsComponent{
state={
expanded: false
}
staticpropTypes={
model: React.PropTypes.object.isRequired,
title: React.PropTypes.string
}
staticdefaultProps={
model: {
id: 0
},
title: ‘Your Name’
}
propTypes和defaultProps是静态属性, 要在组件代码中尽可能高的位置进行声明。
它们作为文档放在醒目的位置, 其他开发者阅读此文件时能立即看到。
所有的组件都应该有propTypes。
方法importReact,{
Component
}
from’react’
import{
observer
}from’mobx-react’
importExpandableFormfrom’./ExpandableForm’
import’./styles/ProfileContainer.css’
exportdefaultclassProfileContainerextendsComponent{
state={
expanded: false
}
staticpropTypes={
model: React.PropTypes.object.isRequired,
title: React.PropTypes.string
}
staticdefaultProps={
model: {
id: 0
},
title: ‘Your Name’
}
handleSubmit=(e)=>{
e.preventDefault() this.props.model.save()
}
handleNameChange=(e)=>{
this.props.model.name =e.target.value
}
handleExpand=(e)=>{
e.preventDefault() this.setState({
expanded: !this.state.expanded
})
}
12345678910111213141516171819202122232425262728293031
importReact,{
Component
}from’react’
import{
observer
}from’mobx-react’
importExpandableFormfrom’./ExpandableForm’
import’./styles/ProfileContainer.css’
exportdefaultclassProfileContainerextendsComponent{
state={
expanded: false
}
staticpropTypes={
model: React.PropTypes.object.isRequired,
title: React.PropTypes.string
}
staticdefaultProps={
model: {
id: 0
},
title: ‘Your Name’
}
handleSubmit= (e) =>{
e.preventDefault() this.props.model.save()
}
handleNameChange= (e) =>{
this.props.model.name = e.target.value
}
handleExpand= (e) =>{
e.preventDefault() this.setState({
expanded: !this.state.expanded
})
}
有了类组件, 在你想子组件传递方法时, 就得去确认它们在被调用到时所持有的this对象是正确的。
这个一般可以通过将this.handleSubmit.bind(this) 传递给子组件来达成。
我们认为这种方式更加干净且容易, 借助ES6的箭头函数可以自动地维护好正确的上线文。
属性析构importReact,{
Component
}
from’react’
import{
observer
}from’mobx-react’
importExpandableFormfrom’./ExpandableForm’
import’./styles/ProfileContainer.css’
exportdefaultclassProfileContainerextendsComponent{
state={
expanded: false
}
staticpropTypes={
model: React.PropTypes.object.isRequired,
title: React.PropTypes.string
}
staticdefaultProps={
model: {
id: 0
},
title: ‘Your Name’
}
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(){
const{
model,
title
}=this.props
return ( < ExpandableFormonSubmit= {
this.handleSubmit
}
expanded= {
this.state.expanded
}
onExpand= {
this.handleExpand
} > < div > < h1 > {
title
} < /h1> <input type=”text” value={model.name} onChange={this.handleNameChange} placeholder=”Your Name” / > < /div> </ExpandableForm > )
}
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
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
}
staticpropTypes= {
model: React.PropTypes.object.isRequired,
title: React.PropTypes.string
}
staticdefaultProps= {
model: {
id: 0
},
title: ‘Your Name’
}
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() {
const {
model,
title
} = this.props
return ( < ExpandableFormonSubmit= {
this.handleSubmit
}
expanded= {
this.state.expanded
}
onExpand= {
this.handleExpand
} > < div > < h1 > {
title
} < / h1 > < input type=”text” value={model.name} onChange={this.handleNameChange} placeholder=”Your Name” / > < / div > < /
ExpandableForm > )
}
}
拥有许多属性的组件要让每个属性都另起一行, 如上所示。
装饰器 @observer
export default class ProfileContainer extends Component {
12 @ observer
exportdefaultclassProfileContainerextendsComponent {
如果你使用了一些像mobx的东西, 就可以像上面这样对类组件进行装饰— 这样做跟将组件传递给一个函数是一样的效果。
装饰器是一种用来修改组件功能的灵活且可读性好的方式。
我们对其进行了广泛的运用, 包括mobx还有我们自己的mobx-models库。
如果你不想使用装饰器, 可以这样做: classProfileContainerextendsComponent {
Componentcode
}
exportdefaultobserver(ProfileContainer) 1 2 3 4 class ProfileContainer extends Component {
Componentcode
}
exportdefaultobserver(ProfileContainer) 闭包要避免向子组件传递新的闭包, 如下: < inputtype=”text”
value= {
model.name
}
onChange= {
(e) => {
model.name =e.target.value
}
} ^ Not this.Use the below: onChange = {
this.handleChange
}
placeholder=”Your Name” / > 1 2 3 4 5 6 7 < inputtype=”text”
value= {
model.name
}
onChange= {
(e) => {
model.name =e.target.value
}
} ^ Not this.Use the below: onChange = {
this.handleChange
}
placeholder=”Your Name” / > 原因: 每次父组件渲染时, 都会有一个新的函数被创建并传递给输入。
如果输入是一个React组件, 不管它的其它属性实际是否已经发生了变化, 都会自动地触发让它重新渲染。
调和是React中消耗最昂贵的部分, 因此不要让它的计算难度超过所需!另外, 传递一个类方法更容易阅读、 调试和修改。
如下是完整的组件代码:importReact, {
Component
} from ‘react’
import {
observer
} from ‘mobx-react’
Separatelocalimportsfromdependencies
importExpandableFormfrom’./ExpandableForm’
import’./styles/ProfileContainer.css’
Usedecorators
ifneeded @observer
exportdefaultclassProfileContainerextendsComponent {
state= {
expanded: false
}
Initializestatehere(ES7) or in a constructor method(ES6) Declare propTypes as static properties as early as possible static propTypes = {
model: React.PropTypes.object.isRequired,
title: React.PropTypes.string
}
DefaultpropsbelowpropTypesstaticdefaultProps= {
model: {
id: 0
},
title: ‘Your Name’
}
Usefatarrowfunctions
formethodstopreservecontext(thiswillthusbethecomponentinstance) 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() {
Destructureprops
forreadability
const {
model,
title
} = this.props
return ( < ExpandableFormonSubmit= {
this.handleSubmit
}
expanded= {
this.state.expanded
}
onExpand= {
this.handleExpand
} > Newline props
if there are more than two < div > < h1 > {
title
} < /h1> <input type=”text” value={model.name} onChange={(e) => { model.name = e.target.value }} Avoid creating new closures in the render method- use methods like below onChange={this.handleNameChange} placeholder=”Your Name” / > < /div> </ExpandableForm > )
}
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
import React, {
Component
} from ‘react’
import {
observer
} from ‘mobx-react’
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
}
Initializestatehere(ES7) orinaconstructormethod(ES6) DeclarepropTypesasstaticpropertiesasearlyaspossiblestaticpropTypes = {
model:React.PropTypes.object.isRequired,
title:React.PropTypes.string
}
DefaultpropsbelowpropTypesstaticdefaultProps= {
model: {
id: 0
},
title: ‘Your Name’
}
Usefatarrowfunctions
formethodstopreservecontext(thiswillthusbethecomponentinstance) 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 ( < ExpandableFormonSubmit= {
this.handleSubmit
}
expanded= {
this.state.expanded
}
onExpand= {
this.handleExpand
} > Newline props
if there are more than two < div > < h1 > {
title
} < / h1 > < input type=”text” value={model.name} onChange={(e) => { model.name = e.target.value }} Avoid creating new closures in the render method- use methods like below onChange={this.handleNameChange} placeholder=”Your Name” / > < / div > < /
ExpandableForm > )
}
}
函数式组件这些组件没有状态和方法。
它们就是单纯的组件, 容易理解。
要尽可能常去使用它们。
propTypesimport React from ‘react’
import {
observer
} from ‘mobx-react’
import ‘./styles/Form.css’
const expandableFormRequiredProps = {
onSubmit: React.PropTypes.func.isRequired,
expanded: React.PropTypes.bool
}
Component declaration ExpandableForm.propTypes = expandableFormRequiredProps 1 2 3 4 5 6 7 8 9
import React from ‘react’
import {
observer
} from ‘mobx-react’
import ‘./styles/Form.css’
const expandableFormRequiredProps = {
onSubmit: React.PropTypes.func.isRequired,
expanded: React.PropTypes.bool
}
Component declaration ExpandableForm.propTypes = expandableFormRequiredProps这里, 我们将 propTypes 分配给了顶部一行的变量。
在组件声明的下面, 我们对它们进行了正常的分配。
对 Props 和 defaultProps 进行析构import React from ‘react’
import {
observer
} from ‘mobx-react’
import ‘./styles/Form.css’
const expandableFormRequiredProps = {
onSubmit: React.PropTypes.func.isRequired,
expanded: React.PropTypes.bool
}

 

function ExpandableForm(props) {
return ( < formstyle= {
props.expanded? {
height: ‘auto’
} : {
height: 0
}
} > {
props.children
} < button onClick = {
props.onExpand
} > Expand < /button> </form > )
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
import React from ‘react’
import {
observer
} from ‘mobx-react’
import ‘./styles/Form.css’
const expandableFormRequiredProps = {
onSubmit: React.PropTypes.func.isRequired,
expanded: React.PropTypes.bool
}

 

function ExpandableForm(props) {
return ( < formstyle= {
props.expanded? {
height: ‘auto’
} : {
height: 0
}
} > {
props.children
} < button onClick = {
props.onExpand
} > Expand < / button > < /
form > )
}
我的组件是一个函数, 因此可以将它的属性看做是参数。
我们可以像下面这样对它们进行扩展: import React from ‘react’
import {
observer
} from ‘mobx-react’
import ‘./styles/Form.css’
const expandableFormRequiredProps = {
onExpand: React.PropTypes.func.isRequired,
expanded: React.PropTypes.bool
}

 

function ExpandableForm({
onExpand,
expanded=false,
children
}) {
return ( < formstyle= {
expanded? {
height: ‘auto’
} : {
height: 0
}
} > {
children
} < button onClick = {
onExpand
} > Expand < /button> </form > )
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
import React from ‘react’
import {
observer
} from ‘mobx-react’
import ‘./styles/Form.css’
const expandableFormRequiredProps = {
onExpand: React.PropTypes.func.isRequired,
expanded: React.PropTypes.bool
}

 

function ExpandableForm({
onExpand,
expanded=false,
children
}) {
return ( < formstyle= {
expanded? {
height: ‘auto’
} : {
height: 0
}
} > {
children
} < button onClick = {
onExpand
} > Expand < / button > < /
form > )
}
注意, 我们也能以一种高度可读的方式使用默认参数来扮演 defaultProps 的角色。
如果 expanded 是 undefined, 我们就会将其设置为 false。
(这个例子有点勉强, 因为是一个布尔值, 不过本身对于避免对象的“ Cannot read < property > of undefined“ 这样的错误是很有用的)。 要避免如下这种 ES6 语法:
const ExpandableForm = ({
onExpand,
expanded,
children
}) => {
1
constExpandableForm= ({
onExpand,
expanded,
children
}) = > {
看着非常现代, 不过这里的函数实际上没有被命令。
这样子的名称在 Bable 进行了正确的设置的情况下是可行的— 但如果没有正确设置, 任何错误都会以在 << anonymous >> 中出现的方式显示, 调试起来相当麻烦。
无名的函数也会在 Jest 这个 React 测试库中引发问题。
为了避免潜在的复杂问题出现, 我们建议使用
function 而不是
const。
封装因为在函数式组件中不能使用装饰器, 所以你可以简单地将它传递到函数中充当参数:
import React from ‘react’
import {
observer
} from ‘mobx-react’
import ‘./styles/Form.css’
const expandableFormRequiredProps = {
onExpand: React.PropTypes.func.isRequired,
expanded: React.PropTypes.bool
}

 

functionExpandableForm({
onExpand,
expanded=false,
children
}) {
return ( < formstyle= {
expanded? {
height: ‘auto’
} : {
height: 0
}
} > {
children
} < button onClick = {
onExpand
} > Expand < /button> </form > )
}
ExpandableForm.propTypes = expandableFormRequiredProps
export default observer(ExpandableForm) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
import React from ‘react’
import {
observer
} from ‘mobx-react’
import ‘./styles/Form.css’
const expandableFormRequiredProps = {
onExpand: React.PropTypes.func.isRequired,
expanded: React.PropTypes.bool
}

 

function ExpandableForm({
onExpand,
expanded=false,
children
}) {
return ( < formstyle= {
expanded? {
height: ‘auto’
} : {
height: 0
}
} > {
children
} < button onClick = {
onExpand
} > Expand < / button > < /
form > )
}
ExpandableForm.propTypes = expandableFormRequiredProps
export default observer(ExpandableForm) 如下是完整的组件代码:
import React from ‘react’
import {
observer
} from ‘mobx-react’
Separate local imports from dependencies
import ‘./styles/Form.css’
Declare propTypes here as a variable,
then assign below
function declaration You want these to be as visible as possible
const expandableFormRequiredProps = {
onSubmit: React.PropTypes.func.isRequired,
expanded: React.PropTypes.bool
}
Destructure props like so,
and use
default arguments as a way of setting defaultProps
function ExpandableForm({
onExpand,
expanded=false,
children
}) {
return ( < formstyle= {
expanded? {
height: ‘auto’
} : {
height: 0
}
} > {
children
} < button onClick = {
onExpand
} > Expand < /button> </form > )
}
Set propTypes down here to those declared above ExpandableForm.propTypes = expandableFormRequiredProps Wrap the component instead of decorating it
export default observer(ExpandableForm) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
import React from ‘react’
import {
observer
} from ‘mobx-react’
Separate local imports from dependencies
import ‘./styles/Form.css’
Declare propTypes here as a variable,
then assign below
function declaration You want these to be as visible as possible
const expandableFormRequiredProps = {
onSubmit: React.PropTypes.func.isRequired,
expanded: React.PropTypes.bool
}
Destructure props like so,
and use
default arguments as a way of setting defaultProps
function ExpandableForm({
onExpand,
expanded=false,
children
}) {
return ( < formstyle= {
expanded? {
height: ‘auto’
} : {
height: 0
}
} > {
children
} < button onClick = {
onExpand
} > Expand < / button > < /
form > )
}
Set propTypes down here to those declared above ExpandableForm.propTypes = expandableFormRequiredProps Wrap the component instead of decorating it
export default observer(ExpandableForm) JSX 中的条件分支你会有不少机会去做许多条件分支渲染。
如下是你想要去避免的情况: 嵌套的三元组并非不是好主意。
有一些库可以解决这个问题(JSX – Control Statements), 不过相比引入额外的依赖, 通过如下这种方式解决复杂条件分支问题要更好: 使用花括弧封装一个 IIFE,
然后在里面放入
if 语句, 可以返回任何你想要渲染的东西。
注意像这样的 IIFE 对性能会有影响, 不过在大多数情况中还不足以让我们为此选择丢掉可读性。
还有就是当你只想要在一个条件分支中渲染一个元素时, 比起这样做… {
isTrue?< p > True! < /p> : <none/>
}
1 2 3 4 5 {
isTrue?< p > True! < / p > : < none/>
}…
使用短路写法更划算。
» 本文来自:前端开发者 » 《编写 React 组件的最佳实践_前端开发者_网站前端开发》
» 本文链接地址:https://www.rokub.com/2293.html
» 您也可以订阅本站:https://www.rokub.com
赞(0)
64K

评论 抢沙发

评论前必须登录!