N'écrivez pas la même chose: comment les composants React réutilisés aideront les développeurs frontaux à créer des applications plus rapidement


Faire les mêmes changements à trois ou quatre endroits différents dans le code JS est un art qui nécessite de l'attention. S'il y a plus d'éléments, le support du code se transforme en farine. Par conséquent, pour des projets à long terme ou de grande envergure, vous devez écrire du code afin qu'il puisse être exécuté dans des composants séparés.

Je suis engagé dans le développement frontal depuis 10 ans et je parlerai de l’utilisation de composants pour créer des éléments frontaux - cela simplifie considérablement la vie d’un développeur frontal.

Écrit avec le soutien de Mail.ru Cloud Solutions .

Quels sont les composants frontaux et pourquoi sont-ils nécessaires


Balises HTML - niveau conditionnellement "zéro" des composants. Chacun d'eux a ses propres fonctions et objectifs.

Les classes CSS sont le prochain niveau d'abstraction qui est généralement atteint lors de la création même d'un petit site. Dans les règles d'application des styles à une classe CSS, nous décrivons le comportement de tous les éléments qui font partie d'un sous-ensemble conditionnel d'éléments.

Les règles qui s'appliquent aux classes CSS, ainsi que tous les autres éléments, tels que les balises HTML, vous permettent de définir et de modifier de manière centralisée les règles d'affichage d'un nombre illimité d'éléments du même type. Il existe différents outils pour travailler avec des styles d'élément - en fait CSS, Sass, LESS, PostCSS et des méthodologies pour appliquer des styles - BEM, SMACSS, CSS atomique, modules CSS, composants stylisés.

En fait, les composants sont:

  • les éléments du même type qui ont à la fois les mêmes styles et la même disposition (HTML) et le même comportement (JS);
  • éléments de style et de comportement similaires qui diffèrent légèrement les uns des autres.

La technologie des composants Web est en cours de développement, ce qui vous permet de créer des balises HTML personnalisées et d'inclure des modèles de code dans la mise en page. Cependant, les composants sont devenus largement utilisés grâce aux frameworks de développement frontaux modernes tels que Angular, Vue, React. Les fonctionnalités JavaScript facilitent la connexion d'un composant:

import {Header, Footer} from "./components/common";
render() {
    return (
       ...
   )
}

Tous les projets majeurs arrivent dans leur bibliothèque de composants prêts à l'emploi ou pour utiliser l'un des composants prêts à l'emploi. La question de savoir quand vous devez passer de la copie de code à la création de composants est décidée individuellement, il n'y a pas de recettes sans ambiguïté.

Il convient de rappeler non seulement l'écriture du code, mais également son support. Un simple copier / coller de la même disposition et des styles isolants dans les classes CSS peut créer un affichage pendant un certain temps sans risques particuliers. Mais si une logique de comportement écrite en JS est ajoutée à chaque élément, les avantages de la réutilisation du code se font littéralement sentir à partir de 2 à 3 éléments, en particulier lorsqu'il s'agit de prendre en charge et de modifier du code précédemment écrit.

Composants React réutilisables


Supposons que notre application soit devenue suffisamment grande et que nous avons décidé d'écrire notre propre bibliothèque de composants. Je suggère d'utiliser pour cela le populaire outil de développement frontal React. L'un de ses avantages est la possibilité d'utiliser simplement et efficacement des composants intégrés. Dans le code ci-dessous, l'ancien composant de l'application utilise trois composants imbriqués: AppHeader, Article, AppFooter:

import React from "react";
import AppHeader from "./components/AppHeader";
import Article from "./components/Article";
import AppFooter from "./components/AppFooter";
export default class App extends React.Component {
    constructor(props) {
        super(props); 
        this.state = {
            title : "My App",
            contacts : "8 800 100 20 30"
           firtsArticleTitle : "Welcome",
           secondArticleTitle : "Let's speak about..."
        }
    };

    render() {
        return (
            <>
                <AppHeader 
                title={this.state.title}
            />
            <Article
                   title={this.state.firstArticleTitle}
               />
               <Article
                   title={this.state.secondArticleTitle}
               />               
               <AppFooter
                   contacts={this.state.contacts}
               />
           </>
       )
   }
}

Veuillez noter : maintenant, il n'est plus nécessaire d'utiliser la balise d'emballage principale dans la mise en page - c'était généralement le cas div. Modern React propose l'outil Fragment, dont un enregistrement abrégé <></>. Dans ces balises, vous pouvez utiliser une hiérarchie de balises plates, comme dans l'exemple ci-dessus.

Nous avons utilisé trois composants de bibliothèque, dont l'un est deux fois dans un bloc. Les données de l'application parente sont transférées vers les accessoires du composant et seront disponibles à l'intérieur de celui-ci via la propriété this.props. Cette approche est typique de React et vous permet d'assembler rapidement une vue à partir d'éléments typiques. Surtout si votre application comporte de nombreuses pages similaires qui ne diffèrent que par le contenu des articles (modèle) et les fonctionnalités.

Cependant, nous devrons peut-être modifier le composant de bibliothèque. Par exemple, les composants ayant la même fonctionnalité peuvent différer non seulement dans le contenu textuel, mais aussi dans la conception: couleur, retraits, bordures. Il est également possible de fournir différentes fonctionnalités d'un même composant.

Le cas suivant est considéré ci-dessous: en fonction de la présence d'un rappel, notre composant peut être «réactif» ou simplement rester une vue pour restituer un élément sur la page:

// App.js
...
render() {
    return (        
        <Article 
            text={this.state.articleText}
            onClick={(e) => this.bindTap(e)}
           customClass={this.state.mainCustomClass}
        />                
    )
}

// Article.js
import React from "react";

export default class Article extends React.Component {
    constructor(props) {
        super(props);         
    };

    render() {
       let cName="default";
       if (this.props.customClass) cName = cName + " " this.props.customClass;
       let bgColor="#fff";
       if (this.props.bgColor) bgColor = this.props.bgColor;
        return (
            {this.props.onClick &&
            <div
                   className={cName}
                onClick={(e) => this.props.onClick(e)}
                   style={{background : bgColor}}
            >
                <p>{this.props.text}<p/>
            </div>
            }
            {!this.props.onClick && 
                <div className={cName}>
                <p>{this.props.text}<p/>
                </div>
           }
        )
    }
} 

Dans React, il existe une autre technique pour étendre les capacités des composants. Dans les paramètres d'appel, vous pouvez transférer non seulement des données ou des rappels, mais également la disposition entière:

// App.js
...
render() {
    return (        
        <Article 
            title={this.state.articleTitle}
            text={
               <>
                <p>Please read the article</p>
                <p>Thirst of all, I should say programming React is a very good practice.</p>
               </>
            }
        />                
    )
}

// Article.js
import React from "react";
export default class Article extends React.Component {
    constructor(props) {
        super(props);         
    };

    render() {
        return (
            <div className="article">
            <h2>{this.props.title}</h2>
            {this.props.text}
            </div>
        )
    }
}

La disposition interne du composant sera reproduite dans son intégralité lors de son transfert props.

Il est souvent plus pratique de transférer une disposition supplémentaire vers le composant de bibliothèque à l'aide du modèle d'insertion et d'utilisation this.props.children. Cette approche est préférable pour modifier les composants communs responsables des blocs typiques d'une application ou d'un site où différents contenus internes sont supposés: casquettes, barres latérales, blocs d'annonces et autres.

// App.js
...
render() {
    return (        
        <Article title={this.state.articleTitle}>
           <p>Please read the article</p>
            <p>First of all, I should say programming React is a very good practice.</p>
       </Article>                          
    )
}

// Article.js
import React from "react";
export default class Article extends React.Component {
    constructor(props) {
        super(props);         
    };

    render() {
        return (
            <div className="article">
            <h2>{this.props.title}</h2>
            {this.props.children}
            </div>
        )
    }
} 

Composants React complets


Les composants responsables uniquement de View ont été examinés ci-dessus. Cependant, nous devrons probablement soumettre aux bibliothèques non seulement la cartographie, mais également la logique standard de traitement des données.

Examinons le composant Téléphone, qui est conçu pour entrer un numéro de téléphone. Il peut masquer le numéro entré à l'aide d'une bibliothèque de validateurs de plug-in et informer le composant senior que le téléphone est entré correctement ou incorrectement:

// Phone.js
import React from "react";
import Validator from "../helpers/Validator";
export default class Phone extends React.Component {
    constructor(props) {
        super(props);   
        this.state = {
            value : this.props.value || "",
            name : this.props.name,
            onceValidated : false,
            isValid : false,
            isWrong : true
        }
        this.ref = React.createRef();    
    };

    componentDidMount = () => {
        this.setValidation();
    };

    setValidation = () => {
        const validationSuccess = (formattedValue) => {
            this.setState({
            value : formattedValue,
            isValid : true,
            isWrong : false,
            onceValidated : true
           });
            this.props.setPhoneValue({
            value : formattedValue, 
            item : this.state.name, 
            isValid : true
            })
        }
        const validationFail = (formattedValue) => {
            this.setState({
            value : formattedValue,
            isValid : false,
            isWrong : true,
            });
            this.props.setPhoneValue({
            value : formattedValue, 
            item : this.state.name, 
            isValid : false
            })
        }
        new Validator({
            element : this.ref.current,
            callbacks : {
            success : validationSuccess,
            fail : validationFail
            }
        });
    }

    render() {
        return (
            <div className="form-group">
            <labeL htmlFor={this.props.name}>
                    <input 
                name={this.props.name}
                id={this.props.name}
                type="tel"
                placeholder={this.props.placeholder}
                defaultValue={this.state.value}
                ref={this.ref}
                />
            </label>
            </div>
        )
    }

} 

Ce composant a déjà un état d'état interne, dont il peut partager une partie avec le code externe qui l'a appelé. L'autre partie reste à l'intérieur du composant, dans l'exemple ci-dessus onceValidated. Ainsi, une partie de la logique du composant y est complètement enfermée.

On peut dire que le comportement typique est indépendant des autres parties de l'application. Par exemple, selon que le numéro a été validé ou non, nous pouvons afficher différentes invites de texte. Nous avons intégré un composant réutilisable distinct non seulement l'affichage, mais aussi la logique de traitement des données.

Composants MT


Si notre composant standard prend en charge des fonctionnalités avancées et possède une logique de comportement suffisamment développée, il convient de le diviser en deux:

  • «Intelligent» pour travailler avec des données ( Model);
  • "Dumb" pour afficher ( View).

La connexion se poursuivra en appelant un composant. Ce sera maintenant Model. La deuxième partie - View- sera appelée render()avec props, dont certaines proviennent de l'application, et l'autre partie est déjà l'état du composant lui-même:

// App.js
...
render() {
    return (        
        <Phone 
            name={this.state.mobilePhoneName}
            placeholder={"You mobile phone"}
        />                
    )
}

// Phone.js
import React from "react";
import Validator from "../helpers/Validator";
import PhoneView from "./PhoneView";
export default class Phone extends React.Component {
    constructor(props) {
        super(props);   
        this.state = {
            value : this.props.value || "",
            name : this.props.name,
            onceValidated : false,
            isValid : false,
            isWrong : true
        }
        this.ref = React.createRef();    
    };

    componentDidMount = () => {
        this.setValidation();
    };

    setValidation = () => {
        const validationSuccess = (formattedValue) => {
            ...
        }
        const validationFail = (formattedValue) => {
            ...
        }
        new Validator({
           element : this.ref.current,
            ...
        });
    }    

   render() {
        return (
            <PhoneView
                name={this.props.name}
            placeholder={this.props.placeholder}
               value={this.state.value}
               ref={this.ref}
            />
        )
    }
}

// PhoneView.js
import React from "react";
const PhoneView = React.forwardRef((props, ref) => (   
    <div className="form-group">
        <labeL htmlFor={props.name}>
            <input 
                name={props.name}
            id={props.name}
            type="tel"
            ref={ref}
            placeholder={props.placeholder}
            value={props.value}                
            />
        </label>
    </div>    
));
export default PhoneView;

Il convient de prêter attention à l'outil React.forwardRef(). Il vous permet de créer refdans un composant Phone, mais de le lier directement aux éléments de disposition dans PhoneView. Toutes les manipulations comme d'habitude refseront alors disponibles en Phone. Par exemple, si nous devons connecter un validateur de numéro de téléphone.

Une autre caractéristique de cette approche est la simplification maximale du Viewcomposant. En fait, cette partie est définie comme const, sans ses méthodes intégrées. Uniquement la mise en page et la substitution des données du modèle.

Maintenant, notre composant réutilisable est divisé en Modelet View, nous pouvons développer séparément la logique métier et le code de mise en page. Nous pouvons également assembler la mise en page à partir d'éléments de composants encore plus petits.

L'état de l'application entière exécutée sur les composants


Il a été montré ci-dessus que l'application peut gérer des composants à la fois en passant des paramètres ou en composant et en utilisant des rappels.

Pour que l'application fonctionne correctement, le niveau supérieur doit recevoir des données significatives sur l'état des composants réutilisés imbriqués. Cependant, ce n'est peut-être pas le niveau le plus élevé de l'application entière.

Si nous avons un bloc d'autorisation client et des composants réutilisables pour y entrer un identifiant et un mot de passe, l'application entière n'a pas besoin de savoir dans quel état se trouvent ces composants simples à un moment donné. Au contraire, le bloc d'autorisation lui-même peut calculer un nouvel état sur la base des états de composants réutilisés simples et le transmettre: le bloc d'autorisation est correctement rempli ou non.

Avec une imbrication importante de composants, il est nécessaire de surveiller l'organisation du travail avec les données afin de toujours savoir où se trouve la "source de vérité". J'ai déjà écrit

sur certaines des difficultés associées à la transition d'état asynchrone dans React . Les composants réutilisables doivent toujours transmettre par le biais des rappels les données nécessaires à la gestion des éventuels blocs de composants. Cependant, vous n'avez pas besoin de transférer des données supplémentaires afin de ne pas provoquer de redessins inutiles de grandes parties de l'arborescence DOM et de ne pas compliquer le code pour le traitement des modifications des composants. Une autre approche de l'organisation des données consiste à utiliser un contexte d'invocation de composants. Il s'agit d'une méthode React native. , disponible à partir de la version 16.3, à ne pas confondre avec une version antérieure ! ..



createContextReact getChildContext

Ensuite, vous n'avez pas besoin de faire passer les accessoires à travers «l'épaisseur» des composants dans l'arborescence d'imbrication des composants. Ou utilisez des bibliothèques spécialisées pour la gestion des données et la livraison des modifications, telles que Redux et Mobx (voir l' article sur le bundle Mobx + React ).

Si nous construisons une bibliothèque de composants réutilisables sur Mobx, chacun des types de ces composants aura son propre magasin. C'est-à-dire la «source de vérité» sur l'état de chaque instance du composant, avec un accès de bout en bout depuis n'importe où dans l'application entière. Dans le cas de Redux et de son seul entrepôt de données, tous les états de tous les composants seront disponibles en un seul endroit.

Quelques bibliothèques prêtes à l'emploi de composants React


Il existe des bibliothèques populaires de composants prêts à l'emploi qui, en règle générale, étaient à l'origine des projets internes d'entreprises:

  1. Material-UI — , Material Design Google.
  2. React-Bootstrap — , . : API , , .
  3. VKUI — «». VK mini apps, (. VK mini apps). VKUI «». «» . vkconnect — iOS Android.
  4. ARUI Feather — React- -. , . open source, .

Toutes ces bibliothèques visent à créer des dispositions d'éléments et leur style. L'interaction avec l'environnement est configurée à l'aide de rappels. Par conséquent, si vous souhaitez créer des composants réutilisables à part entière décrits dans les troisième et quatrième paragraphes de l'article, vous devrez le faire vous-même. Peut-être, en prenant comme composants de la vue de ces composants l'une des bibliothèques populaires présentées ci-dessus.

Cet article a été écrit avec le support de Mail.ru Cloud Solutions .


All Articles