📜 ⬆️ ⬇️

How to search for users on GitHub without React + RxJS 6 + Recompose

This article is the answer to the translation of the article “How to search for users on GitHub using React + RxJS 6 + Recompose” , which just yesterday taught us how to use React, RxJS and Recompose together. Well, I suggest now to see how this can be implemented without these tools.




Disclaimer
It may seem to many that this article contains elements of trolling, written in a hurry and fan ... So, this is so.

')
Since this is a response article, I decided to build it in the same step-by-step format as the original. In addition, the article also aims to compare the original implementation with that described here. Therefore, it contains an abundance of references and quotations from the original. Let's get started

This article is intended for people with experience with React and RxJS. I am only sharing templates that I find useful to create such a UI.

This article is intended for people who have experience with Javascript (ES6), HTML, and CSS. In addition, in my implementation I will use the “disappearing” SvelteJS framework , but it is so simple that you do not need to have experience using it to understand the code.

We do the same thing:



No classes, work with life cycle or setState.

Yes, without classes, working with a life cycle or setState. And also without React, ReactDOM, RxJS, Recompose. And besides, without componentFromStream, createEventHandler, combineLatest, map, startWith, setObservableConfig, BehaviorSubject, merge, of, catchError, delay, filter, map, pluck, switchMap, tap, {another bullshit} ... In short, you understood.

Training


All that is needed is in my REPL example on the SvelteJS website. You can try there or locally by downloading the sources from there (button with a characteristic icon).

To begin, create a simple App.html file, which will be the root component of our widget, with the following content:

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


Hereinafter, I use the styles from the original article. Note that right now they are in scope, i.e. apply only to this component and you can safely use tag names where relevant.

I will write styles directly in the components, because the SFC, and also because the REPL does not support taking out CSS / JS / HTML into different files, although this is easily done using the Svelte preprocessors .

Recompose


We rest ...

Stream component


... sunbathing ...

Configuration


... drinking coffee ...

Recompose + RxJS


... while others ...

Map


... work.

Adding an event handler


Not really a handler of course, just binding:

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


Well, we define the default username:

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


Now, if you start typing something in the input field, the value of username will change.


The problem of eggs and chicken


No chickens, no eggs, no problems with other animals, we do not use RxJS.

Tying together


Everything is already reactive and connected. So continue to drink coffee.

User component


This component will be responsible for displaying the user whose name we will pass on to it. It will receive the value from the App component and translate it into an AJAX request.

“Wow, wow, wow, easy” ©

We have this component will be stupid and just display a beautiful user card by a previously known model. You never know where data can come from and / or where in the interface we want to show this card.

This is what the User.html component will look like:

 <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 just added to the component. Instead of JSX, we have HTMLx embedded in Svelte.

Container


The container is any parent component for the User component. In this case, this is the App component.

debounceTime


It does not make sense to clear the text input if the operation only overwrites the value in the data model. So in the binding we do not need it.

pluck


filter


map


We connect


Let's go back to App.html and import the User component:

 import User from './User.html'; 


Data request


GitHub provides an API for retrieving user information:

For the demonstration, just write a small api.js file that will abstract data acquisition and export the corresponding function:

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


And we also import this function into App.html.

Now we will formulate the problem in a more objective language: we need to change another value when changing one value in the data model (username). Let's name it, respectively, user - user data that we get from the API. Reactivity in all its glory.

To do this, we write the computed Svelte property using the following construction in App.html:

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


Everything, now at change of a user name request for data acquisition on this value will be sent. However, since the binding responds to every change, that is, input in the text field, there will be too many requests and we will quickly exceed all available limits.

In the original article, this problem is solved by the built-in RxJS debounceTime function, which does not allow us to request data too often. For our own implementation, you can use a standalone solution, such as debounce-promise or any other suitable one, since there is something to choose from.

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


So, this lib creates a debounce-version of the function passed to it, which we later use in the calculated property.

switchMap


ajax


RxJS provides its own ajax implementation that works great with switchMap!

Since we do not use RxJS and especially switchMap, we can use any library to work with ajax.

I use axios , because it is convenient for me, but you can use anything and this does not change the essence of the matter.

We try


First, we need to register the User component to use it as a template tag, since the import itself does not add the component to the template context:

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

Despite the seemingly unnecessary scribbling, this allows, among other things, giving different names of tags to instances of the same component, making your templates more semantically understandable.

Further, the value of user is not the data itself, but the promise for this data. Because we are freeloaders and do not want to do any work at all, just drink coffee with cookies.

In order to transfer real data to the User component, we can use a special construction for working with promises:

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

It makes sense to check the data object for existence before passing it to the User component. The Spread operator here allows you to "split" an object into separate props when creating an instance of the User component.



In short, the work.

Error processing


Try entering a non-existent username.
...
Our application is broken.

Your probably yes, but ours definitely not))) Just nothing will happen, although this is certainly not the case.

catchError


Add an additional block for processing rejected promis:

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


Error component


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

Now our UI looks much better:

And do not say, and most importantly no effort.



Load indicator


In short, there further heresy began, with all the Behavior Subjects there and their ilk. We just add the loading indicator and we will not milk the elephant:

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

Result?

Two tiny logic-less components (User and Error) and one control component (App), where the most complex business logic is described in one line - the creation of a calculated property. No daubing observable objects from head to toe and connections +100500 tools that you do not need.

Interactive demo

Write simple and clear code. Write less code, so work less and spend more time with your family. Live!

All happiness and health!

Everything :)


Fyi


If you have already looked at an example in the REPL, then you probably noticed a toast with a warning at the bottom left:
Compiled, but with 1 warning - check the console for details

If you are not too lazy to open the console, you will see the following message:
Unused CSS selector
.user-card .Organization {
background-position: top right;
}

Svelte's static analyzer tells us that some component styles are not used. In addition, according to your desire or the command of the default settings of the compiler, unused styles will be removed from the final css-bundle without your participation.

P / S


Read other articles about Svelte , as well as look into the Russian-language telegram channel SvelteJS . We will be glad to you!

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


All Articles