Express ReactJS or tyrim Angular features in our framework

    In recent times, AngularJS has not criticized only the lazy, and I, over the past couple of months, have read many criticized articles. One such article ended with the phrase “I don’t understand how Angular can be so popular because it is so bad!”
    “And really,” I thought, “if it is so bad, why is it so popular?” And I seem , found the answer. The fact is that Angular, like jQuery, has a low entry threshold, it is simple and intuitive. Yes, of course, you can do complex things in Angular and jQuery, but most people do not use these libraries in this way. What I mean?

    Let's say the developer has the following task: “Make a list of reviews, if there are no reviews, suggest the user to add a review”
    First, let's implement this on Angular:

    Reviews

    {{review.user}}: {{review.text}}
    There are no reviews, wanna add some?


    And now on React:
    var Reviews = React.createClass({
        getReviews (){
            if(!this.props.reviews.length){
                return (
                    
    There are no reviews, wanna add some?
    ) } }, getReview (review){ return (
    {review.user}: {review.text}
    ) }, render (){ return (

    Reviews

    {this.maybeGetAddNew()} {this.props.reviews.map(this.getReview} ) } });


    As we can see, React code is not only longer, but also more confusing. After I finally struck the head of designers and typesetters “Markup is always at the bottom of the file, write plain HTML, just use className instead of class”, the designer goes into my file, sees this and says to me “Are you kidding me?” What kind of braces are these? I need to add a class to one div, and what is this? I actually taught HTML, not JS. ”It's
    another matter with Angular, here the designer understands what needs to be added and where, maybe he even understands that when it disappears and that it repeats, you just need to scare him that if he touches the ng attributes -, then a terrible judgment awaits him, hell and destruction, and everything will be in order.
    Well, these are designers, what to take from them, nubiyo, in a word, we are bearded, harsh programmers, we like this code. Yah? And if someone from the team tells us “Hey {username}, what kind of a naked div is that? He’s breaking the whole layout for me! ”And we go into the component and see this:

     render (){
            return (
                 {this.getPostTitleIfPostHasTitle()}
                 {this.getPostAuthorAvatarIfAuthorHasAvatar()}
                 {this.getPostAuthorNameIfNotAnonymous()}
                 {this.getPostTeaserImageIfPostHasTeaserImage()}
                 {/*ну и так далее*/}
            )
        }
    


    and now we need to run through the methods and check the conditions of each to find the ill-fated div. Well, if some of the methods are also inherited from the mixin ... then there will be unbridled fun for an hour, or even more.

    Decision?


    Obviously, we need to port a couple of the most used Angular directives in React, for example, ng-show, ng-hide, ng-class and ng-repeat. But how? You can, of course, make a mixin that will run along the hardcoded list of "directives" and compare them with the host's prop, and then execute the logic associated with this "directive"

    var DirectiveMixin = {
        renderWithDirectives (target){
            if(!!this.props.isShownWhen){
                 return target;
            }
            return null;
        }
    }
    var Component = {
        mixins: [DirectiveMixin],
        render (){
             return this.renderWithDirectives(
                 
    Hi there!
    ) } } var userIsLoggedIn = false; var React.renderComponent(, document.body);


    Already not bad, but there are a couple of points. First, we must write a component for every small detail that conditionally appears / hides on our site, even if it is a very small span. Well, and secondly, if we want to use this for some old component, we will have to rewrite the render method using renderWithDirectives, and this is not always possible. It’s also difficult to debug if you have a component



    which should appear, but not appear, is it because userIsLoggedIn is false, or does the AdminBar simply not use renderWithDirectives? These problems can be solved by creating one small component, which, for readability, we will call It. It may look something like this:

    var It = React.createClass({
         mixins: [DirectiveComponents],
         render (){
             return this.renderWithDirectives(this.props.children);
         }
    });
    


    And you can use it like this:

    There are no reviews, wanna add some?


    And for even greater readability, you can fill it with syntactic sugar, for example, like this:

    LougoutLoginLougoutLogin


    That's it? We have a mix, It component and syntactic sugar in the form of Show / Hide when / unless, it remains to finish ng-class and ng-repeat and can we use github and npm? Somehow everything is too simple, let's complicate our lives, and not just port the Angular guidelines, but also make them better. For example, like this:

    LogoutLogin
        No account? Sign up!
    There are no reviews, wanna add some?


    There is nowhere more understandable, since it is almost English. Now, the designer will precisely understand what is conditionally appearing / disappearing, and the developer who sees this code for the first time will immediately understand what's what, he will not have to control / click on methods like {this.maybeGetThisStuff ()} to figure it out . And who is unfamiliar with the situation when an evil coder writes an evil ticket to a perplexed developer, “Why does your delimiter appear on a new moon ?! I explained to you a hundred times, it should appear when the post has no tumbnail, in Beijing the air temperature is above 20 degrees or the post has a tumbnail, in London there is a clear sky and the full moon, but it shouldn’t be loved by the new moon! ”Now it can be explained to him that he describes in English in white the behavior of the components already at the layout stage, for example, like this:



    The attentive reader probably already asks me, “Ah, you demon! You came up with a natural language processor, or what? So why are you powdering my head with your React! ”
    Unfortunately, no, I haven’t invented AI, and, apparently, I won’t get the Nobel. The principle of operation of these "string" conditions is similar to the work of translation functions, that is, for example, in this case:

    __('Please like and subscribe!')
    


    String Please like and subscribe! is the key of the associative array, and the translation is carried out by reading the value of this key (if any). The matching __ function in a vacuum probably looks something like this:

    function __(str){
        return 'undefined' == typeof translations[str] ? str : translations[str];
    }
    


    Thus, the phrase post has no thumbnail, it's 20 C in Beijing or the sky is clear in London and the moon is full will serve as the key to the object whose value is Boolean. After the typesetter described the behavior of the component, the programmer needed to finish his coffee and write something like:

    getDesigner2CoderTranslations (){
        return {
             "post has no thumbnail, it's 20 C in Beijing or the sky is clear in London and the moon is full":
                  (!this.postHasThumbnail() && this.getTemperature('Beijing') == 20) || 
                  (this.getSkyState('London') == this.SKY_CLEAR && this.getMoonPhase() == this.MOON_FULL)
        }
    }
    


    Not bad, but where will this object with "translations" be stored, and how will we transfer it? Well, I thought about it and decided that it would be best to use an undocumented React function called context. This is something like props, which are transmitted not from the immediate parent to the immediate child, but from the higher component to all lower ones, that is, children of children, children of children of children, etc. Here is an unofficial introduction .
    And, armed with new knowledge, let's finish:

    childContextTypes: {
         monstroLanguage: React.PropTypes.object
    },
    getChildContext: function() {
        return {
            monstroLanguage: {
                "post has no thumbnail, it's 20 C in Beijing or the sky is clear in London and the moon is full":
                    (!this.postHasThumbnail() && this.getTemperature('Beijing') == 20) || 
                    (this.getSkyState('London') == this.SKY_CLEAR && this.getMoonPhase() == this.MOON_FULL)
            }
        };
    }
    


    Mixin and directives declare that they expect such a context, and all “string” conditions will be searched in it.

    contextTypes: {
        monstroLanguage: React.PropTypes.object
    },
    


    You’re beautifully laying, fraerc. And where can you look at your magic?


    I posted the code on github . Now I’m finishing the documentation (more precisely, I’m translating from bitbucket markdown to github markdown), examples and testing at the same time. By the evening, I think I’ll finish, and put it on npm. I would like to hear your opinion, advice, which directives would not hurt to add. Pull requests, of course, are welcome in every possible way.

    Also popular now: