Comment faire des recherches d'utilisateurs sur GitHub sans React + RxJS 6 + Recompose

Cet article est une réponse à l'article de traduction «Comment rechercher des utilisateurs sur GitHub en utilisant React + RxJS 6 + Recompose» , qui nous a appris hier à utiliser ensemble React, RxJS et Recompose. Eh bien, je propose maintenant de voir comment cela peut être mis en œuvre sans ces outils.




Clause de non-responsabilité
Il peut sembler à beaucoup que cet article contient des éléments de pêche à la traîne, a été écrit à la hâte et sur un fan ... Alors, c'est ainsi.


Puisqu'il s'agit d'un article de retour, j'ai décidé de le construire dans le même format étape par étape que l'original. De plus, l'article vise également à comparer l'implémentation d'origine avec celle décrite ici. Par conséquent, il contient une abondance de références et de citations de l'original. Commençons.

Cet article est destiné aux personnes ayant une expérience avec React et RxJS. Je partage juste des modèles que j'ai trouvés utiles pour créer une telle interface utilisateur.

Cet article est destiné aux personnes ayant une expérience avec Javascript (ES6), HTML et CSS. De plus, dans mon implémentation, j'utiliserai le framework SvelteJS «en voie de disparition» , mais il est si simple que vous n'avez pas besoin d'avoir de l'expérience pour l'utiliser pour comprendre le code.

Nous faisons la même chose:



Sans classes, travailler avec un cycle de vie ou setState.

Oui, sans classes, travaillant avec un cycle de vie ou setState. Et aussi sans React, ReactDOM, RxJS, Recompose. Et d'ailleurs, sans componentFromStream, createEventHandler, combineLatest, map, startWith, setObservableConfig, BehaviorSubject, merge, of, catchError, delay, filter, map, pluck, switchMap, tap, {another bullshit} ... En bref, vous comprenez.

La préparation


Tout ce dont vous avez besoin est dans mon exemple REPL sur le site Web de SvelteJS. Vous pouvez y flairer, soit localement, en téléchargeant la source depuis là (bouton avec une icône caractéristique).

Tout d'abord, créez un simple fichier App.html, qui sera le composant racine de notre widget, avec le contenu suivant:

<input placeholder="GitHub username"> <style> input { font-size: 20px; border: 1px solid black; border-radius: 3px; margin-bottom: 10px; padding: 10px; } </style> 


Ci-après, j'utilise les styles de l'article d'origine. Veuillez noter qu'en ce moment ils sont dans la portée, c'est-à-dire s'appliquent uniquement à ce composant et vous pouvez utiliser en toute sécurité les noms de balises le cas échéant.

J'écrirai des styles directement dans les composants, car SFC, et aussi parce que REPL ne prend pas en charge l'exportation de CSS / JS / HTML vers différents fichiers, bien que cela se fasse facilement à l'aide de préprocesseurs Svelte .

Recomposer


Au repos ...

Composant en ligne


... bronzer ...

La configuration


... boire du café ...

Recomposer + RxJS


... tandis que d'autres ...

La carte


... travail.

Ajouter un gestionnaire d'événements


Pas vraiment un gestionnaire bien sûr, juste une liaison:

 <input bind:value=username placeholder="GitHub username"> 


Eh bien, nous définissons la valeur du nom d'utilisateur par défaut:

 <script> export default { data() { return { username: '' }; } }; </script> 


Maintenant, si vous commencez à taper quelque chose dans le champ de saisie, la valeur du nom d'utilisateur changera.


Problème d'oeufs et de poulet


Pas de poules, pas d'œufs, pas de problèmes avec les autres animaux, nous n'utilisons pas RxJS.

Tricoter ensemble


Tout est déjà réactif et connecté. Alors nous buvons du café.

Composant utilisateur


Ce composant sera chargé d'afficher l'utilisateur dont nous lui transmettrons le nom. Il recevra de la valeur du composant App et le traduira en une demande AJAX.

«Wow wow wow, calme-toi» ©

Chez nous, ce composant sera stupide et affichera simplement une belle carte utilisateur selon un modèle déjà connu. Peu importe d'où les données peuvent provenir et / ou à quel endroit de l'interface nous voulons montrer cette carte.

Le composant User.html ressemblera à ceci:

 <div class="github-card user-card"> <div class="header User" /> <a class="avatar" href="https://github.com/{login}"> <img src="{avatar_url}&s=80" alt={name}> </a> <div class="content"> <h1>{name || login}</h1> <ul class="status"> <li> <a href="https://github.com/{login}?tab=repositories"> <strong>{public_repos}</strong>Repos </a> </li> <li> <a href="https://gist.github.com/{login}"> <strong>{public_gists}</strong>Gists </a> </li> <li> <a href="https://github.com/{login}/followers"> <strong>{followers}</strong>Followers </a> </li> </ul> </div> </div> <style> /*  */ </style> 


Jsx / css


CSS vient d'être ajouté au composant. Au lieu de JSX, nous avons HTMLx intégré dans Svelte.

Conteneur


Le conteneur est tout composant parent du composant utilisateur. Dans ce cas, il s'agit d'un composant de l'application.

debounceTime


Le débogage de la saisie de texte n'a pas de sens si l'opération ne remplace que la valeur dans le modèle de données. Donc, dans la liaison elle-même, nous n'en avons pas besoin.

arracher


filtrer


carte


Connecter


Revenons à App.html et importons le composant utilisateur:

 import User from './User.html'; 


Demande de données


GitHub fournit une API pour récupérer les informations utilisateur:

Pour la démonstration, nous écrivons simplement un petit fichier api.js qui résumera la réception des données et exportera la fonction correspondante:

 import axios from 'axios'; export function getUserCard(username) { return axios.get(`https://api.github.com/users/${username}`) .then(res => res.data); } 


Et juste comme ça, nous importons cette fonction dans App.html.

Nous formulons maintenant le problème dans un langage plus spécifique: nous devons changer une autre valeur lors du changement d'une valeur dans le modèle de données (nom d'utilisateur). Nous l'appellerons respectivement utilisateur - données utilisateur que nous obtenons de l'API. La réactivité dans toute sa splendeur.

Pour ce faire, écrivez une propriété Svelte calculée en utilisant la construction suivante dans App.html:

 <script> import { getUserCard } from './api.js'; ... export default { ... computed: { user: ({ username }) => username && getUserCard(username) } }; </script> 


Voilà, maintenant, lorsque vous changez le nom d'utilisateur, une demande sera envoyée pour recevoir des données sur cette valeur. Cependant, comme les classeurs répondent à chaque modification, c'est-à-dire qu'ils sont entrés dans un champ de texte, il y aura trop de demandes et nous dépasserons rapidement toutes les limites disponibles.

Dans l'article d'origine, ce problème est résolu par la fonction debounceTime intégrée à RxJS, ce qui nous empêche de demander trop de données. Pour notre implémentation, vous pouvez utiliser la solution autonome, telle que debounce-promise ou toute autre solution appropriée, car il y a beaucoup de choix.

 <script> import debounce from 'debounce-promise'; import { getUserCard } from './api.js'; ... const getUser = debounce(getUserCard, 1000); ... export default { ... computed: { user: ({ username }) => username && getUser(username) } }; </script> 


Ainsi, cette bibliothèque crée une version anti-rebond de la fonction qui lui est passée, que nous utilisons ensuite dans la propriété calculée.

switchMap


ajax


RxJS fournit sa propre implémentation ajax qui fonctionne très bien avec switchMap!

Comme nous n'utilisons pas RxJS et surtout switchMap, nous pouvons utiliser n'importe quelle bibliothèque pour travailler avec ajax.

J'utilise axios parce que c'est pratique pour moi, mais vous pouvez utiliser n'importe quoi et cela ne change pas l'essence de la question.

Essayez


Tout d'abord, nous devons enregistrer le composant User pour l'utiliser comme balise de modèle, car l'importation elle-même n'ajoute pas le composant au contexte du modèle:

 <script> import User from './User.html'; ... export default { components: { User } ... }; </script> 

Malgré le griffonnage apparemment excessif, cela permet, entre autres, de donner des noms de balise différents aux instances du même composant, ce qui rend vos modèles plus compréhensibles sémantiquement.

En outre, la valeur utilisateur n'est pas les données elles-mêmes, mais une promesse pour ces données. Parce que nous sommes des freeloaders et que nous ne voulons faire aucun travail, buvez simplement du café avec des cookies.

Afin de transférer des données réelles vers le composant utilisateur, nous pouvons utiliser une conception spéciale pour travailler avec des promesses:

 {#await user} <!--     --> {:then user} <!--      .    . --> {#if user} <User {...user} /> {/if} {/await} 

Il est judicieux de vérifier l'existence de l'objet de données avant de le transmettre au composant utilisateur. L'opérateur Spread vous permet ici de «diviser» un objet en accessoires séparés lors de la création d'une instance du composant User.



Travail plus court.

Gestion des erreurs


Essayez d'entrer un nom d'utilisateur inexistant.
...
Notre application est cassée.

Le vôtre probablement oui, mais le nôtre ne l'est certainement pas))) Rien ne se passera, même si ce n'est certainement pas le cas.

catchError


Ajoutez un bloc supplémentaire pour le traitement des promesses rejetées:

 {#await user} {:then user} {#if user} <User {...user} /> {/if} {:catch error} <Error {...error} /> {/await} 


Erreur de composant


 <div class="error"> <h2>Oops!</h2> <b>{response.status}: {response.data.message}</b> <p>Please try searching again.</p> </div> 

Maintenant, notre interface utilisateur est beaucoup mieux:

Et ne dites pas, et surtout pas d'effort.



Indicateur de chargement


En bref, une nouvelle hérésie a commencé là-bas, avec toutes sortes de sujets comportementaux et d'autres comme eux. Nous ajoutons simplement un indicateur de chargement et nous ne traire pas l'éléphant:

 {#await user} <h3>Loading...</h3> {:then user} {#if user} <User {...user} /> {/if} {:catch error} <Error {...error} /> {/await} 

Résultat?

Deux minuscules composants sans logique (utilisateur et erreur) et un composant de contrôle (application), où la logique métier la plus complexe est décrite en une seule ligne - créant une propriété calculée. Pas de brossage des objets observables de la tête aux pieds et des connexions +100500 outils dont vous n'avez pas besoin.

Démo interactive

Écrivez un code simple et clair. Écrivez moins de code, ce qui signifie moins de travail et plus de temps avec votre famille. Vivez!

Tout bonheur et santé!

Tous :)


Fyi


Si vous avez déjà regardé l'exemple dans REPL, vous avez probablement remarqué le toast avec un avertissement en bas à gauche:
Compilé, mais avec 1 avertissement - consultez la console pour plus de détails

Si vous n'êtes pas trop paresseux pour ouvrir la console, vous verrez ce message:
Sélecteur CSS inutilisé
.user-card .Organization {
position de fond: en haut à droite;
}

Svelte Static Analyzer nous informe que certains styles de composants ne sont pas utilisés. De plus, à votre demande ou à la commande des paramètres de compilation par défaut, les styles inutilisés seront supprimés du bundle css final sans votre participation.

P / s


Lisez d' autres articles sur Svelte , ainsi que la chaîne de télégrammes en langue russe SvelteJS . Nous serons ravis de vous voir!

Source: https://habr.com/ru/post/fr419653/


All Articles