Heim >Web-Frontend >js-Tutorial >React-Komponentenprojekt (ausführliches Tutorial)
Dieser Artikel zeigt Ihnen den gesamten Prozess des Schreibens von React-Komponentenprojektpraktiken anhand von Beispielen. Freunde, die daran interessiert sind, können sich darauf beziehen.
Als ich anfing, React zu schreiben, sah ich viele Möglichkeiten, Komponenten zu schreiben. Es gibt hundert Möglichkeiten, hundert Tutorials zu schreiben. Obwohl React selbst ausgereift ist, scheint es keinen „richtigen“ Weg zu geben, es zu verwenden. Deshalb fasse ich (der Autor) hier die Erfahrungen mit der Verwendung von React zusammen, die unser Team im Laufe der Jahre gesammelt hat. Ich hoffe, dieser Artikel ist für Sie nützlich, egal ob Sie Anfänger oder Veteran sind.
Wir verwenden die ES6- und ES7-Syntax. Wenn Sie sich über den Unterschied zwischen Anzeigekomponenten und Containerkomponenten nicht ganz im Klaren sind, empfehlen wir Ihnen, zunächst diesen Artikel zu lesen. Wenn Sie Vorschläge oder Fragen haben, hinterließ Duqing eine Nachricht in den Kommentaren zu klassenbasierten Komponenten
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.
Status initialisieren
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 mit der richtigen Funktion 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
Es ist tatsächlich 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 nicht weiß, welchen Wert es hat.
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 kennen, wie z. B. mobx, können Sie das obige Beispiel verwenden, um Klassenkomponenten zu dekorieren. Ein Dekorator ist eine Methode, die eine Klassenkomponente als Parameter übergibt.
Dekoratoren ermöglichen das Schreiben flexiblerer und lesbarerer Komponenten. Wenn Sie keinen Dekorator verwenden möchten, können Sie Folgendes tun:
class ProfileContainer extends Component { // Component code } export default observer(ProfileContainer)
Abschluss
Versuchen Sie, Abschlüsse in untergeordneten Komponenten zu vermeiden, z :
<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 das Neuzeichnen ausgelöst, unabhängig davon, ob sich andere 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 Status, 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 defaultProps 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
表达式都放进去。返回你要返回的组件。
上面是我整理给大家的,希望今后会对大家有帮助。
相关文章:
vue.js element-ui tree树形控件改iview的方法
Das obige ist der detaillierte Inhalt vonReact-Komponentenprojekt (ausführliches Tutorial). Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!