Maison  >  Article  >  interface Web  >  Projet de composant React (tutoriel détaillé)

Projet de composant React (tutoriel détaillé)

亚连
亚连original
2018-06-01 15:18:061347parcourir

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.

Avant de commencer :

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.

Méthodes

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 });

setStateC'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 &#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

Nous définissons propTypes avant la déclaration du composant.

Décomposer les Props et defaultProps

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>
 )
}

Notre composant est une méthode. Son paramètre est props. Nous pouvons étendre ce composant comme ceci :

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>
 )
}

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 &#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表达式都放进去。返回你要返回的组件。

上面是我整理给大家的,希望今后会对大家有帮助。

相关文章:

vue 父组件调用子组件方法及事件

vue.js element-ui tree树形控件改iview的方法

javascript实现文件拖拽事件

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!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn