Maison >interface Web >js tutoriel >Projet de composant React (tutoriel détaillé)
Cet article partage avec vous l'ensemble du processus d'écriture des pratiques du projet de composants React à travers des exemples. Les amis qui sont intéressés par cela peuvent s'y référer.
Quand j'ai commencé à écrire React, j'ai vu de nombreuses façons d'écrire des composants. Il existe cent façons d’écrire une centaine de tutoriels. Bien que React lui-même ait mûri, il ne semble pas y avoir de « bonne » façon de l’utiliser. Je (l'auteur) résume donc ici l'expérience d'utilisation de React que notre équipe a accumulée au fil des années. J'espère que cet article vous sera utile, que vous soyez débutant ou vétéran.
Nous utilisons la syntaxe ES6 et ES7. Si vous n'êtes pas très clair sur la différence entre les composants d'affichage et les composants de conteneur, il est recommandé de commencer par lire cet article. Si vous avez des suggestions ou des questions, Duqing a laissé un message dans les commentaires sur les composants basés sur les classes
De nos jours, les composants React sont généralement développés à l'aide de composants basés sur les classes. Ensuite, nous écrirons notre composant sur la même ligne :
import React, { Component } from 'react'; import { observer } from 'mobx-react'; import ExpandableForm from './ExpandableForm'; import './styles/ProfileContainer.css';
J'aime beaucoup le CSS en javascript. Cependant, cette méthode d’écriture des styles est encore trop nouvelle. Nous introduisons donc des fichiers CSS dans chaque composant. De plus, les importations introduites localement et les importations mondiales seront séparées par une ligne vide.
Initialiser l'état
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 }
Vous pouvez utiliser l'ancienne méthode pour initialiser constructor
dans state
. Des informations complémentaires peuvent être trouvées ici. Mais nous choisissons une approche plus claire.
De plus, nous avons veillé à ajouter export default
devant la classe. (Note du traducteur : bien que cela puisse ne pas être correct lors de l'utilisation de Redux).
propTypes et 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
et defaultProps
sont des propriétés statiques. Définissez-le le plus tôt possible dans la classe du composant afin que les autres développeurs puissent le remarquer immédiatement lors de la lecture du code. Ils peuvent servir de documentation.
Si vous utilisez React 15.3.0 ou une version ultérieure, vous devez importer le package prop-types
séparément au lieu d'utiliser React.PropTypes
. Plus de contenu se déplace ici.
Tous vos composants doivent avoir des types d'accessoires.
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 }) } // ... }
Dans les composants de classe, lorsque vous transmettez des méthodes aux composants enfants, vous devez vous assurer qu'ils sont appelés en utilisant le bon this. Cela se fait généralement lors du passage à un composant enfant : this.handleSubmit.bind(this)
.
Il est beaucoup plus simple d’utiliser la méthode des flèches d’ES6. Il maintient automatiquement le contexte correct (this
).
Passez une méthode à setState
Dans l'exemple ci-dessus, il y a cette ligne :
this.setState({ expanded: !this.state.expanded });
setState
C'est en fait asynchrone ! Afin d'améliorer les performances, React appellera plusieurs setState
ensemble. Par conséquent, l'état peut ne pas changer immédiatement après l'appel de setState
.
Ainsi, lorsque vous appelez setState
, vous ne pouvez pas vous fier à la valeur de l'état actuel. Parce que je ne connais pas du tout sa valeur.
Solution : Passez une méthode à setState
et transmettez la valeur de l'état avant d'appeler en paramètre cette méthode. Jetez un oeil à l'exemple :
this.setState(prevState => ({ expanded: !prevState.expanded }))
Merci à Austin Wood pour l'aide.
Démonter les composants
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}
Pour les multilignes props
, chaque accessoire doit occuper une ligne distincte. Tout comme l'exemple ci-dessus. La meilleure façon d'y parvenir est d'utiliser un ensemble d'outils : Prettier
.
Decorator
@observer export default class ProfileContainer extends Component {
Si vous connaissez certaines bibliothèques, telles que mobx, vous pouvez utiliser l'exemple ci-dessus pour modifier les composants de classe. Un décorateur est une méthode qui passe un composant de classe en paramètre.
Les décorateurs vous permettent d'écrire des composants plus flexibles et plus lisibles. Si vous ne souhaitez pas utiliser de décorateur, vous pouvez faire ceci :
class ProfileContainer extends Component { // Component code } export default observer(ProfileContainer)
Fermeture
Essayez d'éviter de passer des fermetures dans les composants enfants, tels que :
<input type="text" value={model.name} // onChange={(e) => { model.name = e.target.value }} // ^ Not this. Use the below: onChange={this.handleChange} placeholder="Your Name"/>
Remarque : Si input
est un composant React, cela déclenchera automatiquement son redessin, que d'autres accessoires aient ou non changé.
La vérification de cohérence est la partie la plus gourmande en ressources de React. N'ajoutez pas de travail supplémentaire ici. La meilleure façon de gérer le problème dans l’exemple ci-dessus est de transmettre une méthode de classe, qui sera plus lisible et plus facile à déboguer. Par exemple :
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"/>
Composant méthode
Ce type de composant n'a pas d'état, pas d'accessoires et pas de méthodes. Ce sont des composants purs et contiennent le moins de contenu provoquant des modifications. Utilisez-les souvent.
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
Nous définissons propTypes
avant la déclaration du composant.
Décomposer les Props et defaultProps
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> ) }
Notre composant est une méthode. Son paramètre est props
. Nous pouvons étendre ce composant comme ceci :
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> ) }
Maintenant, nous pouvons également utiliser les paramètres par défaut pour jouer le rôle d'accessoires par défaut, ce qui a une bonne lisibilité. Si expanded
n'est pas défini, alors nous le définissons sur false
.
Cependant, essayez d'éviter d'utiliser des exemples comme celui-ci :
const ExpandableForm = ({ onExpand, expanded, children }) => {
Cela a l'air moderne, mais la méthode n'a pas de nom.
如果你的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的方法
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!