5 bonnes pratiques d'utilisation des hameçons React en production

L'auteur de l'article, dont nous publions la traduction aujourd'hui, indique que commercetools a adopté les crochets React au début de 2019 - au moment de leur apparition dans React 16.8.0. Depuis lors, les programmeurs de l'entreprise ont constamment traité leur code, le traduisant en crochets. Les crochets React vous permettent d'utiliser l'état des composants et d'utiliser d'autres fonctionnalités React sans utiliser de classes. À l'aide de crochets, vous pouvez, lorsque vous travaillez avec des composants fonctionnels, vous «connecter» aux événements du cycle de vie des composants et réagir aux changements de leur état.



En bref sur les résultats de la mise en œuvre des crochets


Si, en résumé, nous parlons de ce que les crochets nous ont donné, il s'avère qu'ils ont contribué à simplifier la base de code en extrayant la logique des composants et en facilitant la composition de différentes capacités. De plus, l'utilisation des crochets a conduit au fait que nous avons beaucoup appris. Par exemple, comment mieux structurer votre code grâce à l'amélioration continue des fonctionnalités existantes. Nous sommes sûrs que nous apprendrons beaucoup de choses plus intéressantes tout en continuant à travailler avec les crochets React.

Forces de mise en œuvre du crochet


Voici ce que l'introduction des crochets nous a donné:

  • Amélioration de la lisibilité du code. Ceci est possible grâce à l'utilisation d'arbres de composants plus petits qu'auparavant. La création de tels arbres de composants est facilitée par notre désir d'abandonner les propriétés de rendu et les composants d'ordre supérieur.
  • Amélioration des capacités de débogage de code. Nous avions à notre disposition une représentation visuelle améliorée du code et des informations de débogage supplémentaires fournies par les outils de développement React.
  • Amélioration de la modularité du projet. En raison de la nature fonctionnelle des crochets, le processus de création et d'application d'une logique adaptée à une utilisation répétée a été simplifié.
  • Séparation des responsabilités. Les composants React sont responsables de l'apparence des éléments d'interface et les hooks permettent d'accéder à la logique du programme encapsulé.

Maintenant, nous voulons partager nos recommandations sur l'utilisation des crochets en production.

1. Récupérez en temps opportun les crochets des composants et créez des crochets personnalisés


Commencer à utiliser des crochets dans les composants fonctionnels est assez facile. Par exemple, nous avons rapidement postulé quelque part React.useState, quelque part - React.useEffectet avons continué à faire notre travail. Cette approche, cependant, ne permet pas de profiter pleinement de toutes les opportunités que les hameçons sont capables de donner. Présentant React.useStatesous la forme d'un petit use<StateName>State, et React.useEffect- sous la forme use<EffectName>Effect, nous avons pu atteindre les objectifs suivants:

  1. Les composants sont plus petits qu'avec des crochets ordinaires.
  2. Les crochets peuvent être réutilisés dans divers composants.
  3. , React, , . , State<StateName> State. React.useState, .

La récupération des crochets contribue également au fait que la logique réutilisable et partagée est plus visible dans diverses parties de l'application. Une logique similaire ou en double est plus difficile à remarquer si vous n'utilisez que des crochets intégrés au code du composant. Les crochets résultants peuvent être de petite taille et contenir très peu de code useToggleState. D'un autre côté, des crochets plus gros, comme useProductFetcher, peuvent désormais inclure plus de fonctionnalités. Ces deux éléments nous ont aidés à simplifier la base de code en réduisant la taille des composants React.

L'exemple suivant crée un petit crochet React conçu pour contrôler la sélection des éléments. Les avantages de l'encapsulation de cette fonctionnalité deviennent évidents immédiatement après avoir réalisé à quelle fréquence une telle logique est utilisée dans l'application. Par exemple, pour sélectionner un ensemble de commandes dans une liste.

//  

function Component() {
    const [selected, setSelected] = React.useState()
    const select = React.useCallback(id => setSelected(/** ... */), [
        selected,
        setSelect
    ])

    return <List selectedIds={selected} onSelect={id => select(id)} />
}

//   

const useSelection = () => {
    const [selected, setSelected] = React.useState()
    const select = React.useCallback(id => setSelected(/** ... */), [
        selected,
        setSelect
    ])

    return [selected, select]
}

function Component() {
    const [selected, select] = useSelection()

    return <List selectedIds={selected} onSelect={id => select(id)} />
}

2. À propos des avantages de React.useDebugValue


Le crochet React.useDebugValue standard fait référence aux fonctionnalités React peu connues. Ce hook peut aider le développeur lors du débogage du code; il peut être utile pour l'utiliser dans des hooks à accès partagé. Il s'agit de hooks utilisateur utilisés dans de nombreux composants de l'application. Cependant, il n'est useDebugValuepas recommandé de l'utiliser dans tous les hooks utilisateur, car les hooks intégrés enregistrent déjà les valeurs de débogage standard.

Imaginez créer un crochet personnalisé conçu pour prendre une décision sur la nécessité d'activer une fonctionnalité de l'application. Les données sur l'état de l'application sur lesquelles la décision est basée peuvent provenir de différentes sources. Ils peuvent être stockés dans un objet de contexte React, dont l'accès est organisé via React.useContext.

Afin d'aider le développeur à déboguer, tout en travaillant avec les outils de développement React, il peut être utile de connaître le nom d'indicateur analysé ( flagName) et la variante de la valeur d'indicateur ( flagVariation). Dans ce cas, l'utilisation d'un petit crochet peut nous aider React.useDebugValue:

export default function useFeatureToggle(flagName, flagVariation = true) {
    const flags = React.useContext(FlagsContext)
    const isFeatureEnabled = getIsFeatureEnabled(flagName, flagVariation)(flags)

    React.useDebugValue({
        flagName,
        flagVariation,
        isEnabled: isFeatureEnabled
    })

    return isFeatureEnabled
}

Maintenant, en travaillant avec les outils du développeur React, nous pouvons voir des informations sur l'option de la valeur de l'indicateur, sur le nom de l'indicateur et si la fonctionnalité qui nous intéresse est activée ou non.


Utilisation des outils de développement React après avoir appliqué le hook React.useDebugValue

Veuillez noter que dans les cas où les hooks utilisateur utilisent des hooks standard commeReact.useStateouReact.useRef, ces hooks enregistrent déjà le statut correspondant ou les données ref-object. Par conséquent, l'utilisation de constructions de vues iciReact.useDebugValue({ state })n'est pas particulièrement utile.

3. La combinaison et la composition des crochets


Lorsque nous avons commencé à mettre en œuvre des crochets dans notre travail et à utiliser de plus en plus de crochets dans les composants, il s'est rapidement avéré que 5 à 10 crochets pouvaient être présents dans le même composant. Dans ce cas, des crochets de différents types ont été utilisés. Par exemple, nous pourrions utiliser 2-3 hooks React.useState, puis un hook React.useContext(par exemple, pour obtenir des informations sur l'utilisateur actif), un hook React.useEffectet également des hooks provenant d'autres bibliothèques, comme react-router ou react-intl .

Ce qui précède a été répété encore et encore, en conséquence, même de très petits composants n'étaient pas si compacts. Afin d'éviter cela, nous avons commencé à extraire ces crochets individuels en crochets utilisateur, dont le périphérique dépendait du composant ou d'une certaine capacité d'application.

Imaginez un mécanisme d'application visant à créer une commande. Lors du développement de ce mécanisme, de nombreux composants ont été utilisés, ainsi que des crochets de différents types. Ces crochets peuvent être combinés en tant que crochet personnalisé, augmentant l'utilisabilité des composants. Voici un exemple de combinaison d'un ensemble de petits crochets en un seul crochet.

function OrderCreate() {
    const {
        orderCreater,
        orderFetcher,
        applicationContext,
        goToNextStep,
        pendingOrder,
        updatePendingChanges,
        revertPendingChanges
    } = useOrderCreate()

    return (/** ...children */)
}

4. Comparaison de React.useReducer et React.useState


Nous avons souvent eu recours à React.useStateun crochet standard pour travailler avec l'état des composants. Cependant, au fil du temps, l'état du composant peut devoir être compliqué, ce qui dépend des nouvelles exigences pour le composant, comme la présence de plusieurs valeurs dans l'état. Dans certains cas, l'utilisation React.useReducerpeut aider à éviter d'avoir à utiliser plusieurs valeurs et à simplifier la logique de mise à jour de l'état. Imaginez la gestion des états lorsque vous effectuez des requêtes HTTP et recevez des réponses. Pour ce faire, vous devrez peut - être travailler avec des valeurs telles que isLoading, dataeterror. Au lieu de cela, l'état peut être contrôlé à l'aide d'un réducteur, ayant à sa disposition diverses actions pour gérer les changements d'état. Cette approche, idéalement, encourage le développeur à percevoir l'état de l'interface sous la forme d'une machine à états.

Le réducteur transmis React.useReducerest similaire aux réducteurs utilisés dans Redux , où le système reçoit l'état actuel de l'action et doit retourner l'état suivant. L'action contient des informations sur son type, ainsi que des données sur la base desquelles l'état suivant est formé. Voici un exemple d'un réducteur simple conçu pour contrôler un certain compteur:

const initialState = 0;
const reducer = (state, action) => {
    switch (action) {
        case 'increment': return state + 1;
        case 'decrement': return state - 1;
        case 'reset': return 0;
        default: throw new Error('Unexpected action');
    }
};

Ce réducteur peut être testé isolément puis utilisé dans le composant en utilisant React.useReducer:

function Component() {
    const [count, dispatch] = React.useReducer(reducer, initialState);

    return (
        <div>
            {count}
            <button onClick={() => dispatch('increment')}>+1</button>
            <button onClick={() => dispatch('decrement')}>-1</button>
            <button onClick={() => dispatch('reset')}>reset</button>
        </div>
    );
};

Ici, nous pouvons appliquer ce que nous avons examiné dans les trois sections précédentes, à savoir, nous pouvons tout extraire useCounterReducer. Cela améliorera le code en masquant les informations de type d'action d'un composant qui décrit l'apparence d'un élément d'interface. En conséquence, cela aidera à empêcher la fuite des détails d'implémentation dans le composant et nous donnera également des opportunités supplémentaires pour le débogage du code. Voici à quoi ressemblera le hook personnalisé résultant et le composant l'utilisant:

const CounterActionTypes = {
    Increment: 'increment',
    Decrement: 'decrement',
    Reset: 'reset',
}

const useCounterReducer = (initialState) => {
    const [count, dispatch] = React.useReducer(reducer, initialState);

    const increment = React.useCallback(() => dispatch(CounterActionTypes.Increment));
    const decrement = React.useCallback(() => dispatch(CounterActionTypes.Decrement));
    const reset = React.useCallback(() => dispatch(CounterActionTypes.Reset));

    return {
        count,
        increment,
        decrement
    }
}

function Component() {
    const {count, increment} = useCounterReducer(0);

    return (
        <div>
            {count}
            <button onClick={increment}>+1</button>
        </div>
    );
};

5. Mise en place progressive des crochets


À première vue, l'idée d'introduire progressivement des crochets peut ne pas sembler tout à fait logique, mais ici, je suggère que ceux qui le pensent suivent simplement mon raisonnement. Au fil du temps, divers modèles trouvent leur application dans la base de code. Dans notre cas, ces modèles incluent des composants d'ordre supérieur, des propriétés de rendu et maintenant des crochets. Lors de la traduction d'un projet vers un nouveau modèle, les développeurs ne cherchent pas à réécrire instantanément tout le code, ce qui, en général, est presque impossible. Par conséquent, vous devez développer un plan de transfert du projet vers les hooks React, qui ne prévoit pas de modifications majeures du code. Cette tâche peut être très difficile, car apporter des modifications au code entraîne généralement une augmentation de sa taille et de sa complexité. En introduisant des crochets, nous nous efforçons d'éviter cela.

Notre base de code utilise des composants basés sur les classes et des composants fonctionnels. Quel que soit le composant utilisé dans une certaine situation, nous avons cherché à partager la logique via les hooks React. Tout d'abord, nous implémentons la logique dans les hooks (ou répétons l'implémentation existante de certains mécanismes en eux), puis créons de petits composants d'un ordre supérieur, à l'intérieur desquels ces hooks sont utilisés. Après cela, ces composants d'ordre supérieur sont utilisés pour créer des composants basés sur les classes. En conséquence, nous avons à notre disposition une logique située en un seul endroit, qui peut être utilisée dans des composants de différents types. Voici un exemple d'implémentation de la fonctionnalité de hook dans les composants via des composants d'ordre supérieur.

export const injectTracking = (propName = 'tracking') => WrappedComponent => {
    const WithTracking = props => {
        const tracking = useTracking();

        const trackingProp = {
            [propName]: tracking,
        };

        return <WrappedComponent {...props} {...trackingProp} />;
    };

    WithTracking.displayName = wrapDisplayName(WrappedComponent, 'withTracking');

    return WithTracking;
};

export default injectTracking;

Cela montre l'implémentation de la fonctionnalité de raccordement useTrackingdans un composant WrappedComponent. Cela nous a d'ailleurs permis, entre autres, de séparer les tâches d'implémentation des hooks et de réécriture des tests dans les anciennes parties du système. Avec cette approche, nous avons encore à notre disposition des mécanismes pour utiliser des crochets dans toutes les parties de la base de code.

Sommaire


Voici quelques exemples de la façon dont l'utilisation des crochets React a amélioré notre base de code. Nous espérons que les crochets profiteront également à votre projet.

Chers lecteurs! Utilisez-vous des crochets React?


All Articles