Thursday, May 28, 2015

Back from ReactJS - React design pattern

If we are going to try using the React.js in the next product, a result of trial and error and how can I the design, since arrived to the conclusion is cool in its own way, it is the memo.


As it were made, whether these things and Flux! That we will see to that. (Some arrangements are being and I think.)

Until vague "Hmm, I see .." Flux you did not somehow only understood in like a feeling actually is realized that was a super-revolutionary paradigm shift has received quite the shock.

By the way, I think in the programming of context and he either Event or Exception to have come up first how much e, in this case it is of course that of the Event is. 
If the all is Exception, it is those who stop using immediately good.

The Flux

Flux is I think people there that you have seen so figure that is referenced as you may say always many in the description of, it is that of such architecture.


Points ReactViews, ActionCreators, Dispatcher, the arrow of a circle formed from the Store is not only flows in one direction.

Detailed description is I should Kurere read the official site, but personally Dispatcher, View might Madashimo ActionCreator, the impression that the term Store had inhibited the understanding is it does not come isnt focus.

If you replace in your own words ActionCreator, Store are both Model. And, Dispatcher is what the singleton of EventEmitter.

We will look at whether in fact become what implemented in this way of thinking.

EventService

The following is the complete source of EventService of prototype.

Since it uses the WebPack, but uses the node.js Ppoku module.exports, contents are together, even if you write as JS of normal client-side.

event.service.js
  var EventEmitter = require ("wolfy87-eventemitter");

 function EventService () {
   this. on ("error", function (source, data) {
     . console log ("!!! error !!!", source, data);
   });
 }

 . EventService prototype = new EventEmitter ();

 EventService. Prototype. Error = function (source, data) {
   . this emit ("error", source, data);
 };

 . module exports = new EventService ();

event.constant.js
  module. exports = {
   USER_SIGNIN: "user.signin",
   USER_FAIL_SIGNIN: "user.failSignin",
   USER_SIGNOUT: "user.signout",
   ERROR: "error"
 };


We have added the error method for debugging as SyntaxSugar, but substantially EventEmitter is as it is. And what if "var EventService = new EventEmitter ()" We do not matter only one line.

This guy will aggregate all events in the application as a singleton of Dispatcher. Since the literal event name is used dispersed in various places is not cool, event names are aggregated to a constant class.

API

Excerpt Subsequently API

api.js
  var $ = require ("jquery");

 function API () {
   . this endpoint = "";
 }

 API. Prototype. Exec = function (method, path, data) {
   return $. ajax ({
     url:. this endpoint + path,
     type: method,
     data: data
   });
 };

 API. Prototype. Signin = function (nameOrEmail, password) {
   return this. exec ("POST", "/ api / auth / signin", {
     nameOrEmail: nameOrEmail,
     password: password
   });
 };

 API. Prototype. Signout = function () {
   . return this exec ("GET", "/ api / auth / signout");
 };

 . module exports = new API ();

We decided to use the jQuery so are familiar to the library of communication.  It just returns the Promise as a return value to kick the API in short $ .ajax.

Point is not to pinch the extra processing at all, earnestly that you are devoted to be Sutras the API provided by the server. (Common error process might be added later.) What to do in response to the result of the execution of the API, that part is the role of the Model or the View.

By the way endpoint is usually as an empty, but communicates with the Origin server that generated the content, it will be able to carry out the front-end development without a server Simply slide the URL by preparing a development server that supports CORS. (That we put in the prospectus, but in fact it has not tried yet. Perhaps webpack-dev-server You thought that it might not you use, but its story is also them how much work out.)

Model

Signin as an example, it shows a simplified version of the UserService to process signout.

user.service.js
  var $ = require ("jquery");
 var api = require ("../api/API");
 var EventService = require ("./event.service");
 var Events = require ("../constants/event.constants");

 function UserService () {
   var user;
   function getAuthorizedUser () {
     return user;
   }
   function setAuthorizedUser (v) {
     user = v;
   }
   function signin (nameOrEmail, password) {
     api. signin (nameOrEmail, password). done (function (data) {
       if (data. code == 200) {
         . user = data user;
         . EventService emit (.. Events SIGNIN, data user);
       } Else {
         . EventService emit (. Events FAIL_SIGNIN);
       }
     }). Fail (function (xhr) {
       . EventService error ("UserService.signin", xhr);
     });
   }
   function signout () {
     if (user) {
       api. signout (). done (function (data) {
         user = null;
         . EventService emit (. Events SIGNOUT);
       }). Fail (function (xhr) {
         . EventService error ("UserService.signout", xhr);
       });
     }
   }
   $. Extend (this, {
     getAuthorizedUser: getAuthorizedUser,
     setAuthorizedUser: setAuthorizedUser,
     signin: signin,
     signout: signout
   });
 }

 . module exports = new UserService ();


getAuthorizedUser is a method that returns the user who is logged in.

setAuthorizedUser If put the first load (or reload) at the time already logged in user of HTML, it is the method used to set it.

Signin / signout can run the API, you have to ignite after events that set the user in response to the results.

Point is that these Action methods nothing returns as the return value. 

In you are and signin method accustomed to the design of synchronous API will want to return the User object, but it is impossible in the asynchronous processing.
So here returns a Promise. . . Is processed and begin spear will become unnecessarily complicated.
So, you do not have the return value simply by firing the events in these Action methods.

In other words, the caller but can not know whether he had succeeded in actually signin despite calling the UserService # signin, this graciousness is the largest liver of Flux design.
you can watch a separate signin events in EventService If you want to know the results of the signin.

(In this way because the process at the time of fail is always going to be the same when you organize this is still looks good is better to have a common treatment in the API side.)

View

First, it will show the user component that controls the user's signin / signout. 

This component is an image that is as user icon of the drop-down form in the top-right corner of the screen Qiita and GitHub, but here for the sake of simplicity.

  • Show user name and signout button if already signin
  • and display a link to the signin screen if you have not signin
It is with. (Css it does not take into account.)

user.component.jsx
  var React = require ("react");
 . var Navigation = require ('react-router') Navigation;
 . var Link = require ('react-router') Link;
 var UserService = require ("../models/user.service");
 var EventService = require ("../models/event.service");
 var Events = require ("../constants/event.constant");

 module. exports = React. createClass ({
   mixins: [Navigation],
   getInitialState: function () {
     return {
       user:. UserService getAuthorizedUser ()
     };
   },
   onSigninOrSignup: function (user) {
     this. setState ({
       user: user
     });
     . this transitionTo ("home");
   },
   onSignout: function () {
     this. setState ({
       user: null
     });
     . this transitionTo ("home");
   },
   signout: function () {
     . UserService signout ();
   },
   componentDidMount: function () {
     . EventService on (.. Events USER_SIGNIN, this onSigninOrSignup);
     . EventService on (.. Events USER_SIGNUP, this onSigninOrSignup);
     . EventService on (.. Events USER_SIGNOUT, this onSignout);
   },
   componentWillUnmount: function () {
     . EventService off (.. Events USER_SIGNIN, this onSigninOrSignup);
     . EventService off (.. Events USER_SIGNUP, this onSigninOrSignup);
     . EventService off (.. Events USER_SIGNOUT, this onSignout);
   },
   render: function () {
     .. var user = this state user;
     if (user) {
       return <div>
         <Div> {user. Name} </ div>
         <Div> <button onClick = {this. Signout}> MSG. Signout </ button> </ div>
       </ Div>
     } Else {
       return <div> <Link to = "signin"> MSG. signin </ Link> </ div>
     }
   }
 });


You are running UserService # signout by clicking the signout button, but here it does not conduct the switching of the display. 

In fact whether you have succeeded in signout than say that you should not change the display in the parting does not know at this stage.

Switching of the display is done in there picking up (the result generated by the call of UserService # signout) signout events.

Signin / signup is not processing this component is done, but we are the switching of the display can be picked up even those events.

Registration / cancellation of various events listener is the theory conducted at componentDidMount / componentWillUnmount.

By the way out from the right course, but MSG is a global object of only defined the key and string. It is possible to switch between Japanese and English by switching the js file referenced in the HTML side.

/i18n/ja/messages.js
  var MSG = {
   "Signin": "sign-in",
   "Signout": "Sign Out",
   "Username": "user name",
   "Password": "Password",
   "Format": function (fmt) {
     for (i = 1; i <arguments length;. i ++) {
       var reg = new RegExp ("\\ {" + (i - 1) + "\\}", "g")
       . fmt = fmt replace (reg, arguments [i]);
     }
     return fmt;
   }
 }

The other one case, it also shows the source of the signin.

signin.component.jsx
 var React = require ("react / addons"); var Link = require ('react-router') Link;. var UserService = require ("../models/user.service"); var EventService = require (". ./models/event.service "); var Events = require (" ../constants/event.constant "); var Alert = require (" ./alert.components.jsx ");.. module exports = React createClass ({mixins: [.. React addons LinkedStateMixin], getInitialState: function () {return {nameOrEmail: "", password: "", message: ""};}, signin:. function () {UserService signin (this. ... state nameOrEmail, this state password);}, onFailSignin: function () {this setState ({message: MSG signinFailed..});}, componentDidMount:.. function () {EventService on (Events USER_FAIL_SIGNIN, this. onFailSignin);}, componentWillUnmount: function () {EventService off (Events USER_FAIL_SIGNIN, this onFailSignin);}, render:.... function () {return <div> <h2> {MSG signin} </ h2> <ul > <li> <label> {MSG. usernameOrEmail} </ label> <input type = "text" valueLink = {this. linkState ('nameOrEmail')} /> </ li> <li> <label> {MSG. password} </ label> <input type = "password" valueLink = {this. linkState ('password')} /> </ li> </ ul> <button onClick = {this. signin}> {MSG. signin} </ button> <Alert message = {this state message..}> </ Alert> <hr /> <Link to = "signup"> {MSG signup.} </ Link> </ div>}});

In sigin components we are running UserService # signin method based on the input of the user, but at the time of success does not handle the result. That's because doing the user component.

However, failure of signin (such as a password mistake) is there in the event it is necessary to display on this screen will not display the error message is picked up. (Alert is another component that was created because of an error display.)

Thus View components
  • Do something in response to user Action
  • Do something in response to the events of his interest
It is not sufficient to implement each to think of the two separately. A result of user Action as signout, even if the event corresponding to it are obvious may occur, and should never be implemented by mixing the two.

Flux rethink

The above is a design pattern that you are going to attempt to use when making the application in the coming React.

We will organize that the various modules are doing here again signin as an example.
  • Click on the signin button from View
  • Run signin action of Model
  • Model issued a signin events
Starting from the Event of "signin click of a button" by the user, the process flow is interrupted once by now.

And it and is not there is a process flow separately starting from the Event that you are due to Dispatcher "firing of signin events".
  • Dispatcher is firing the signin events
  • Run the action Model or View correspond
If you look at the conceptual view of a side Flux can be reached here, you can see that you have followed the Flux cycle of fully one-way when look at the series of these processing.

Honest, but during trial and error it does not had a strong awareness of the Flux up there, or this kind of thing and Flux to see what was finished! I was feeling strong because. (Because maybe I ... (^^ ;;; regular document firmly not read I think that detail is also the place where different, but outline it should not be removed.)

Summary

Event-driven is a paradigm that is from a long time ago but, such as Java EventListener Since the instance of the various components is managing the event, there was a portion that is not fully become a loosely coupled eventually.
It is possible to create as a component that was autonomous and independent of the various View Components In this way, for example, "the button the user component of signout also put in another place." The existing components even when there is like a change and impact correction range because there is no I can localize.
Personally, this is fine revolutionary paradigm shift.

If you have thought in comparison with Angular, probably amount of code that must be written better of the React will increase.

Or personally Angular can create how code amount less app even using any means, is thought to be a framework that pursues the proposition that, as a result or is not consistent, "Is there such a do? I also think there is aggressive part of brute force is being used, such as I think. "

One React + Flux will feel that it has become possible to maintain the application simple by rule of the one-way data flow. 

Speaking of either when the product has been growing complexity increases, I do it why not overwhelmingly such person of Angular.

If you make such as business systems and services inside for management system of I think more of Angular is facing, but if the consumer app I think Arica even to try using the React. 

It does not mention in this article, (such as be replaced at a later time, for example Alert) benefits of component-oriented is also advantageous in making intense consumer application of change.

No comments:

Post a Comment