Heim > Artikel > Web-Frontend > Über die Praxis von React-Komponentenprojekten
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.
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 });
setState
Tatsä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 '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
Wir definieren propTypes
vor der Deklaration der Komponente.
Props und Standardpropps zerlegen
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 ( <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 '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 ( <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 '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 ( <form style={formStyle} onSubmit={onSubmit}> {children} <button onClick={onExpand}>Expand</button> </form> ) } export default observer(ExpandableForm)
只能这样处理: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 ( <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
表达式都放进去。返回你要返回的组件。
相关推荐:
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!