Heim  >  Artikel  >  Web-Frontend  >  Über die Praxis von React-Komponentenprojekten

Über die Praxis von React-Komponentenprojekten

小云云
小云云Original
2018-03-05 09:13:541261Durchsuche

Bevor Sie beginnen:

Wir verwenden die ES6- und ES7-Syntax. Wenn Sie sich über den Unterschied zwischen Anzeigekomponenten und Containerkomponenten nicht im Klaren sind, empfehlen wir Ihnen, zunächst diesen Artikel zu lesen Wenn Sie Vorschläge oder Fragen haben, wenden Sie sich bitte an Hinterlassen Sie einen Kommentar zu klassenbasierten Komponenten in den Kommentaren.

Heutzutage werden React-Komponenten im Allgemeinen mithilfe klassenbasierter Komponenten entwickelt. Als nächstes schreiben wir unsere Komponente in die gleiche Zeile:


import React, { Component } from 'react';
import { observer } from 'mobx-react';

import ExpandableForm from './ExpandableForm';
import './styles/ProfileContainer.css';

Ich mag CSS in Javascript sehr. Allerdings ist diese Methode des Schreibstils noch zu neu. Deshalb führen wir in jeder Komponente CSS-Dateien ein. Darüber hinaus werden lokal eingeführte Importe und globale Importe durch eine Leerzeile getrennt.

Initialisierungsstatus


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 }

Sie können die alte Methode verwenden, um constructor in state zu initialisieren. Weitere relevante Informationen finden Sie hier. Aber wir wählen einen klareren Ansatz.

Außerdem haben wir darauf geachtet, vor der Klasse export default hinzuzufügen. (Anmerkung des Übersetzers: Obwohl dies bei Verwendung von Redux möglicherweise nicht korrekt ist.)

propTypes und defaultProps


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 und defaultProps sind statische Eigenschaften. Definieren Sie es so früh wie möglich in der Komponentenklasse, damit andere Entwickler es beim Lesen des Codes sofort bemerken können. Sie können als Dokumentation dienen.

Wenn Sie React 15.3.0 oder höher verwenden, müssen Sie das prop-types-Paket separat importieren, anstatt React.PropTypes zu verwenden. Weitere Inhalte werden hierher verschoben.

Alle Ihre Komponenten sollten Requisitentypen haben.

Methoden


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({ expanded: !this.state.expanded })
 }

 // ...

}

Wenn Sie in Klassenkomponenten Methoden an untergeordnete Komponenten übergeben, müssen Sie sicherstellen, dass diese korrekt aufgerufen werden. Dies geschieht normalerweise, wenn es an eine untergeordnete Komponente übergeben wird: this.handleSubmit.bind(this).

Es ist viel einfacher, die Pfeilmethode von ES6 zu verwenden. Es behält automatisch den richtigen Kontext bei (this).

Übergeben Sie eine Methode an setState

Im obigen Beispiel gibt es diese Zeile:


this.setState({ expanded: !this.state.expanded });

setStateTatsächlich , es ist Asynchron! Um die Leistung zu verbessern, ruft React mehrere setState zusammen auf. Daher ändert sich der Status möglicherweise nicht sofort nach dem Aufruf von setState.

Wenn Sie also setState aufrufen, können Sie sich nicht auf den aktuellen Statuswert verlassen. Weil ich seinen Wert überhaupt nicht kenne.

Lösung: Übergeben Sie eine Methode an setState und übergeben Sie den Statuswert, bevor Sie diese Methode als Parameter aufrufen. Schauen Sie sich das Beispiel an:


this.setState(prevState => ({ expanded: !prevState.expanded }))

Danke an Austin Wood für die Hilfe.

Komponenten zerlegen


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}

) } }

Bei mehrzeiligen props sollte jede Requisite eine separate Zeile belegen. Genau wie das obige Beispiel. Der beste Weg, dies zu erreichen, ist die Verwendung einer Reihe von Tools: Prettier.

Decorator


@observer
export default class ProfileContainer extends Component {

Wenn Sie einige Bibliotheken wie mobx kennen, können Sie diese verwenden Verwenden Sie das obige Beispiel, um die Klassenkomponente zu ändern. Ein Dekorator ist eine Methode, die eine Klassenkomponente als Parameter übergibt.

Dekoratoren ermöglichen das Schreiben flexiblerer und lesbarerer Komponenten. Wenn Sie keinen Dekorateur verwenden möchten, können Sie Folgendes tun:


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


Abschluss

Vermeiden Sie die Verwendung von Übergeben Sie den Abschluss in die Komponente, z. B.:


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

Hinweis: Wenn input eine React-Komponente ist, wird dadurch automatisch deren Neuzeichnung ausgelöst. Unabhängig davon, ob sich die Requisiten geändert haben.

Konsistenzprüfung ist der ressourcenintensivste Teil von React. Fügen Sie hier keine zusätzliche Arbeit hinzu. Der beste Weg, das Problem im obigen Beispiel zu lösen, besteht darin, eine Klassenmethode zu übergeben, die besser lesbar und einfacher zu debuggen ist. Zum Beispiel:


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 ( 
   
    // 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"/>

) } }

Methodenkomponente

Dieser Komponententyp hat keinen Zustand, keine Requisiten und keine Methoden. Sie sind reine Komponenten und enthalten die geringste Menge an Inhalten, die Veränderungen verursachen. Benutze sie oft.

propTypes


import React from &#39;react&#39;
import { observer } from &#39;mobx-react&#39;
import { func, bool } from &#39;prop-types&#39;
import &#39;./styles/Form.css&#39;
ExpandableForm.propTypes = {
 onSubmit: func.isRequired,
 expanded: bool
}
// Component declaration

Wir definieren propTypes vor der Deklaration der Komponente.

Props und Standardpropps zerlegen


import React from &#39;react&#39;
import { observer } from &#39;mobx-react&#39;
import { func, bool } from &#39;prop-types&#39;
import &#39;./styles/Form.css&#39;

ExpandableForm.propTypes = {
 onSubmit: func.isRequired,
 expanded: bool,
 onExpand: func.isRequired
}

function ExpandableForm(props) {
 const formStyle = props.expanded ? {height: &#39;auto&#39;} : {height: 0}
 return (
  <form style={formStyle} onSubmit={props.onSubmit}>
   {props.children}
   <button onClick={props.onExpand}>Expand</button>
  </form>
 )
}

Unsere Komponente ist eine Methode. Sein Parameter ist props. Wir können diese Komponente wie folgt erweitern:


import React from &#39;react&#39;
import { observer } from &#39;mobx-react&#39;
import { func, bool } from &#39;prop-types&#39;
import &#39;./styles/Form.css&#39;

ExpandableForm.propTypes = {
 onSubmit: func.isRequired,
 expanded: bool,
 onExpand: func.isRequired
}

function ExpandableForm({ onExpand, expanded = false, children, onSubmit }) {
 const formStyle = expanded ? {height: &#39;auto&#39;} : {height: 0}
 return (
  <form style={formStyle} onSubmit={onSubmit}>
   {children}
   <button onClick={onExpand}>Expand</button>
  </form>
 )
}

Jetzt können wir auch Standardparameter verwenden, um die Rolle von Standard-Requisiten zu spielen, was eine gute Lesbarkeit bietet. Wenn expanded nicht definiert ist, setzen wir es auf false.

Vermeiden Sie jedoch Beispiele wie dieses:


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

Es sieht modern aus, aber die Methode ist unbenannt.

如果你的Babel配置正确,未命名的方法并不会是什么大问题。但是,如果Babel有问题的话,那么这个组件里的任何错误都显示为发生在 a8093152e673feb7aba1828c43532094里的,这调试起来就非常麻烦了。

匿名方法也会引起Jest其他的问题。由于会引起各种难以理解的问题,而且也没有什么实际的好处。我们推荐使用function,少使用const

装饰方法组件

由于方法组件没法使用装饰器,只能把它作为参数传入别的方法里。


import React from &#39;react&#39;
import { observer } from &#39;mobx-react&#39;
import { func, bool } from &#39;prop-types&#39;
import &#39;./styles/Form.css&#39;

ExpandableForm.propTypes = {
 onSubmit: func.isRequired,
 expanded: bool,
 onExpand: func.isRequired
}

function ExpandableForm({ onExpand, expanded = false, children, onSubmit }) {
 const formStyle = expanded ? {height: &#39;auto&#39;} : {height: 0}
 return (
  <form style={formStyle} onSubmit={onSubmit}>
   {children}
   <button onClick={onExpand}>Expand</button>
  </form>
 )
}
export default observer(ExpandableForm)

只能这样处理:export default observer(ExpandableForm)

这就是组件的全部代码:


import React from &#39;react&#39;
import { observer } from &#39;mobx-react&#39;
import { func, bool } from &#39;prop-types&#39;
// Separate local imports from dependencies
import &#39;./styles/Form.css&#39;

// 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: &#39;auto&#39; } : { height: 0 }
 return (
  <form style={formStyle} onSubmit={onSubmit}>
   {children}
   <button onClick={onExpand}>Expand</button>
  </form>
 )
}

// Wrap the component instead of decorating it
export default observer(ExpandableForm)

条件判断

某些情况下,你会做很多的条件判断:


<p id="lb-footer">
 {props.downloadMode && currentImage && !currentImage.video && currentImage.blogText
 ? !currentImage.submitted && !currentImage.posted
 ? <p>Please contact us for content usage</p>
  : currentImage && currentImage.selected
   ? <button onClick={props.onSelectImage} className="btn btn-selected">Deselect</button>
   : currentImage && currentImage.submitted
    ? <button className="btn btn-submitted" disabled>Submitted</button>
    : currentImage && currentImage.posted
     ? <button className="btn btn-posted" disabled>Posted</button>
     : <button onClick={props.onSelectImage} className="btn btn-unselected">Select post</button>
 }
</p>

这么多层的条件判断可不是什么好现象。

有第三方库JSX-Control Statements可以解决这个问题。但是与其增加一个依赖,还不如这样来解决:


<p id="lb-footer">
 {
  (() => {
   if(downloadMode && !videoSrc) {
    if(isApproved && isPosted) {
     return <p>Right click image and select "Save Image As.." to download</p>
    } else {
     return <p>Please contact us for content usage</p>
    }
   }

   // ...
  })()
 }
</p>

使用大括号包起来的IIFE,然后把你的if表达式都放进去。返回你要返回的组件。

相关推荐:

React组件性能优化方法解答

React组件生命周期实例分析

构建React组件最全方法

Das obige ist der detaillierte Inhalt vonÜber die Praxis von React-Komponentenprojekten. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn