博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
如何“正确”编写React组件?我们总结了一套满意的方法
阅读量:4085 次
发布时间:2019-05-25

本文共 8464 字,大约阅读时间需要 28 分钟。

https://www.sdk.cn/news/6505?utm_source=tuicool&utm_medium=referral

摘要: 当我第一次开始写React代码时,我记得看到过许多不同的编写组件的方法,各教程之间有很大的不同。虽然自那时以来框架已经相当成熟,但似乎还没有一个确定“正确”编写的方式。在过去一年里,在MuseFind,我们的团队编写了很多React组件。我们已经逐渐完善了方法,直到我们满意为止。本指南代表我们建议的最佳做法。我们认为本文对新手和老手都有所帮助。

当我第一次开始写React代码时,我记得看到过许多不同的编写组件的方法,各教程之间有很大的不同。虽然自那时以来框架已经相当成熟,但似乎还没有一个确定“正确”编写的方式。

在过去一年里,在 MuseFind ,我们的团队编写了很多React组件。我们已经逐渐完善了方法,直到我们满意为止。

本指南代表我们建议的最佳做法。我们认为本文对新手和老手都有所帮助。

阅读本文之前,读者需要注意以下几点:

  • 我们使用ES6和ES7语法。

  • 如果你不确定展示型组件和容器组件之间的区别,我们建议你先移步阅读这篇文章( )。

  • 如果您有任何建议问题或反馈,请在原文的评论区中告诉我们(  )。

一、基于类的组件

基于类的组件是有状态的,或许还包含方法。我们尽可能少地使用它们,但它们也有自己的位置。

下面,就让我们逐行构建组件。

1.导入CSS

import React, {Component} from 'react'import {observer} from 'mobx-react'import ExpandableForm from './ExpandableForm'import './styles/ProfileContainer.css'

理论上,我喜欢 CSS in JavaScript 。但它仍然是一个新的想法,还没有出现一个成熟的解决方案。在此之前,我们将一个CSS文件导入到每个组件。

我们还通过换行将依赖导入与本地导入分开。

2.初始化状态

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 }

如果你使用ES6(ES7不适用),在构造函数中初始化状态。否则,使用专用于ES7的方法。

我们还要确保将我们的类导出为默认类。

3.propTypes和defaultProps

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 } static propTypes = { model: React.PropTypes.object.isRequired, title: React.PropTypes.string } static defaultProps = { model: { id: 0 }, title: 'Your Name' }

propTypes和defaultProps是静态属性,在组件代码中声明的优先级尽可能高。由于它们作为文档,因此它们应该对其他读取文件的开发者可见。

所有的组件应该有propTypes。

4.方法

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 }  static propTypes = {    model: React.PropTypes.object.isRequired,    title: React.PropTypes.string  }  static defaultProps = {    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的箭头函数自动保持正确的上下文。

5.解构props

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 }  static propTypes = {    model: React.PropTypes.object.isRequired,    title: React.PropTypes.string  }  static defaultProps = {    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 (       

{title}

) }}

具有多个props的组件,每个props应该占据单独一个行,如上所示。

6.装饰器

@observerexport default class ProfileContainer extends Component {

如果你使用像 mobx 这样的东西,你可以将类组件装饰成这样:这与将组件传递到函数相同。

装饰器 通过灵活、可读的方式来修改组件功能。我们广泛地使用装饰器,配合mobx和我们自己的 mobx-models 库。

如果您不想使用装饰器,请执行以下操作:

class ProfileContainer extends Component {  // Component code}export default observer(ProfileContainer)

7.闭包

避免传递新的闭包到子组件,像这样:

 { model.name = e.target.value }}            // ^ Not this. Use the below:            onChange={
this.handleChange} placeholder="Your Name"/>

这就是为什么每次父组件渲染时,创建一个新的函数并传递给输入的原因。

如果输入是React组件,这将自动触发它重新渲染,而不管它的其他props是否实际改变。

调和算法(Reconciliation)是React最耗时的部分。不要让它比所需更难!此外,传递类的方法更容易阅读、调试和更改。

这是我们的完整组件:

import React, {Component} from 'react'import {observer} from 'mobx-react'// Separate local imports from dependenciesimport ExpandableForm from './ExpandableForm'import './styles/ProfileContainer.css'// Use decorators if needed@observerexport 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: React.PropTypes.object.isRequired,    title: React.PropTypes.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 (       
// 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"/>
) }}

二、函数组件

这些组件没有状态、方法。它们是纯粹的,简单的。因此要尽可能经常使用它们。

1.propTypes

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 declarationExpandableForm.propTypes = expandableFormRequiredProps

这里,我们在组件声明前分配 propTypes,因此它们立即可见。在组件声明下面,我们正确地分配它们。

2.解构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 (    
{props.children}
)}

我们的组件是一个函数,它的Props作为其参数。我们可以这样扩展:

import React from 'react'import {observer} from 'mobx-react'import './styles/Form.css'ExpandableForm.propTypes = {  onExpand: React.PropTypes.func.isRequired,  expanded: React.PropTypes.bool}function ExpandableForm({ onExpand, expanded = false, children }) {  return (    
{children}
)}

注意,我们也可以使用默认参数作为defaultProps以高度可读的方式。如果展开没有定义的话,我们将其设置为false。(这是一个有点强迫的例子,因为它是一个布尔,但非常有用,以避免“无法读取未定义”错误与对象)。

避免使用以下ES6语法:

const ExpandableForm = ({ onExpand, expanded, children }) => {
`

看起来很现代,但此处的函数实际上是未命名的。

如果你的Babel设置正确,这个名字的缺失不会成为一个问题:但如果不是,任何错误将<<anonymous>>中显示,对调试而言,是一个非常严重的问题。

未命名的函数也可能导致Jest(一个React测试库)的问题。由于潜在的难以理解的bug,以及并没有什么真正的好处,我们建议使用_function_而不是_const_。

3.包装

因为你不能使用装饰器和功能组件,你只需将函数作为参数传递给它:

import React from 'react'import {observer} from 'mobx-react'import './styles/Form.css'ExpandableForm.propTypes = {  onExpand: React.PropTypes.func.isRequired,  expanded: React.PropTypes.bool}function ExpandableForm({ onExpand, expanded = false, children }) {  return (    
{children}
)}export default observer(ExpandableForm)

这是我们的完整组件:

import React from 'react'import {observer} from 'mobx-react'// Separate local imports from dependenciesimport './styles/Form.css'// Declare propTypes here, before the component (taking advantage of JS function hoisting)// You want these to be as visible as possibleExpandableForm.propTypes = {  onSubmit: React.PropTypes.func.isRequired,  expanded: React.PropTypes.bool}// Destructure props like so, and use default arguments as a way of setting defaultPropsfunction ExpandableForm({ onExpand, expanded = false, children }) {  return (    
{children}
)}// Wrap the component instead of decorating itexport default observer(ExpandableForm)

三、JSX条件

很可能你要做很多条件渲染。这里是你想避免的地方:

这是我在MuseFind早期写的实际代码,饶恕我吧。

不,嵌套的三元运算并不是一个好主意。

有一些库解决了这个问题( JSX控制语句 ),但是,而不是引入另一个依赖,我们解决了这种应对复杂条件的方法:

以上所示是重构版本。

使用花括号括起一个 IIFE ,然后把你的if语句置于里面,返回任何你想要的渲染。请注意,这样的IIFE可能会导致性能下降,但在大多数情况下,它不会严重到以致失去可读性。

此外,当你只想渲染一个条件上的元素,而不是这样做:

{  isTrue   ? 

True!

:
}

使用short-circuit赋值:

{  isTrue &&     

True!

}

转载地址:http://pehni.baihongyu.com/

你可能感兴趣的文章
Android 源码编译make的错误处理
查看>>
linux环境下C语言中sleep的问题
查看>>
ubuntu 12.04 安装 GMA3650驱动
查看>>
新版本的linux如何生成xorg.conf
查看>>
xorg.conf的编写
查看>>
启用SELinux时遇到的问题
查看>>
virbr0 虚拟网卡卸载方法
查看>>
No devices detected. Fatal server error: no screens found
查看>>
新版本的linux如何生成xorg.conf
查看>>
Centos 6.0_x86-64 终于成功安装官方显卡驱动
查看>>
Linux基础教程:CentOS卸载KDE桌面
查看>>
read humor_campus
查看>>
IBM WebSphere Commerce Analyzer
查看>>
my read work
查看>>
db db2 base / instance database tablespace container
查看>>
hd disk / disk raid / disk io / iops / iostat / iowait / iotop / iometer
查看>>
project ASP.NET
查看>>
db db2_monitorTool IBM Rational Performace Tester
查看>>
OS + Unix Aix telnet
查看>>
IBM Lotus
查看>>