adding a modern twist to legacy web applications

57
Adding a Modern Twist to Legacy Web Applications Process, Toolset and Buy In by Jeff Dutra - @JeffDutraCanada

Upload: jeff-durta

Post on 17-Aug-2015

71 views

Category:

Software


0 download

TRANSCRIPT

Adding a Modern Twist to Legacy Web Applications

Process, Toolset and Buy In

by Jeff Dutra - @JeffDutraCanada

Housekeeping

All the code will be on GitHub https://github.com/jefferydutra/

AddingModernTwistToLegacyApps

Who Am I

o It does not really matter

o Not an Authority

Who Am I

o software developer/amateur skeptic/pretend psychologist

o Proficient Web Developer working towards becoming an Expert

Introduction

Manage dependencies and build your JavaScript with Node.js, Gulp, Browserify

Adding modern web functionality with React and Flux Strategies for Buy In to start using these toolsets now (or in some reasonable amount of time)

But Why?

Avoid misery of working with legacy code We will see how you can add independent and isolated

components to existing pages; pages that may be difficult to change

React and Flux allow you to make self-contained additions that handle their own data access/persistence

Go Time

GULP

&

JSHINT

What is Gulp

• Grabs some files

• Modify them

• Output new files

A build system should only handle basic functionality and allow other libraries to do things they are made to do.

Why build with Gulp?

o Minify

o Concatenate

o JSHint

o Compile LESS or SASS

o Run your tests (now there is no excuse when it comes to writing tests)

Why not Grunt

o Code over configuration

o Grunt tries do everything itself

o Gulp relies on an eco-system of plug-ins

o Faster

o Cooler logo

The 4 functions Gulp provides

o gulp.task(name[, deps], fn)

o gulp.src(globs[, options])

o gulp.dest(path[, options])

o gulp.watch(glob[, opts], tasks)

globs are a pattern or an array of patterns for file matching. **/*.js = find all files that end in .js

JSHint Task

var gulp = require('gulp');var jshint = require('gulp-jshint');var stylish = require('jshint-stylish');var notify = require("gulp-notify");

gulp.task('jshint', function () { return gulp.src("./js/library/src/**/*.js") .pipe(jshint('.jshintrc')) .pipe(jshint.reporter(stylish)) .pipe(notify(function (file) { if (file.jshint.success) { // Don't show something if success return false; }

var errors = file.jshint.results.map(function (data) { if (data.error) { return "(" + data.error.line + ':' + data.error.character + ') ' + data.error.reason; } }).join("\n");

return file.relative + " (" + file.jshint.results.length + " errors)\n" + errors; }));

JsHint Task Demo(using AirBnb style guide)

What is Browserify?

o Tool for compiling node-flavored commonjs modules for the browser

o Allows you to nicely organize your code

o Promotes code modularity

What are commonjs modules?

o Were created in the early days of server side JavaScript

o Three main variables:

o require

o exports

o module

CommonJs

var numberGreaterThanOrEqualTo = require('./numberGreaterThanOrEqualTo');

console.log(numberGreaterThanOrEqualTo(4,2));

Declaration of numberGreaterThanOrEqualTo.js

Usage of module

var numberGreaterThanOrEqualTo = function( value, testValue ){ if(isNaN(value)){ return false; } if(isNaN(testValue)){ return true; }

return Number(value) >= Number(testValue);};

module.exports = numberGreaterThanOrEqualTo;

Commonjs/Browserify moduleDemo

Tools

WebstormNode for visual studio

What is React

Just the UI Virtual DOM One way reactive data flow

Why React

o You can try it out incrementally

o Facebook/Instagram actually use it on their important products.

oAll about composition

Why React Contd.

oOne way data-binding

o Performance

oNot just the web

o Server sider rendering

Why not the other guys

oAngular

oBackbone

o Ember

oDurandal/Aurelia

o Knockout

Who is using it/migrating to it?

o Khan Academy

o AirBnB

o Yahoo mail

o Flipboard canvas

o Github (issue viewer)

o Atalassian HipChat rewrite

What is the Virtual DOM?

o Copy of the actual DOM

o When any change happens re-render everything to virtual DOM

o Has its own diff algorithm to learn what has changed

o Only update real DOM with changes only

JSX FTW!

var HelloMessage = React.createClass({ render: function() { return ( <div className='thisCanNotBeRight'> Hello {this.props.name} </div>); }});

React.render(<HelloMessage name="John" />, mountNode);

Benefits of not Data-Binding (JSX)

o JsHint,JSCS your code

oMinification

o Type Checking

o Testable

Think in React

o Break up your User Interface into hierarchical pieces

o Create a static version of your of your interface

o Stake out a basic representation of your state

o Decide where your state should live

State and Props

o Props are how you pass data to a child/owned component

o State is the internal state of module

oBoth trigger a re-render

State

• this.setState({

mykey: 'my value'

});

o var value = this.state.myKey;

o Should have one source of truth

Component Specs

oReactElement render()

o object getInitialState()

o object propTypes

o array mixins

o object statics

propTypes

propTypes: { // You can declare that a prop is a specific JS primitive. By default, these // are all optional. optionalArray: React.PropTypes.array, optionalString: React.PropTypes.string, optionalUnion: React.PropTypes.oneOfType([ React.PropTypes.string, React.PropTypes.number, React.PropTypes.instanceOf(Message) ]),

// An array of a certain type optionalArrayOf: React.PropTypes.arrayOf(React.PropTypes.number), optionalObjectOf: React.PropTypes.objectOf(React.PropTypes.number), optionalObjectWithShape: React.PropTyes.shape({ color: React.PropTypes.string, fontSize: React.PropTypes.number }), requiredFunc: React.PropTypes.func.isRequired, customProp: function(props, propName, componentName) { if (!/matchme/.test(props[propName])) { return new Error('Validation failed!'); } }

* When an invalid value is provided for a prop, a warning will be shown in the JavaScript console. Note that for performance reasons propTypes is only checked in development mode.

Component Specs

var React = require("React");var Router = require('react-router');

var Sample = React.createClass({ mixins: [ Router.Navigation, Router.State ], propTypes: { optionalString: React.PropTypes.string }, getInitialState: function() { return {optionalString: this.props.optionalString}; }, render: function(){ return ( <div className="row"> {this.state.optionalString} </div> ); }});

module.exports = Sample;

Lifecycle Methods

o componentWillMount

o componenetDidMount

o componentWillReceiveProps

o shouldComponentUpdate

o componentWillUpdate

o componentDidUpdate

o componentWillUnmount

Lifecycle Methods

componentWillMount

Invoked once, both on the client and server, immediately before the initial rendering occurs.

invoked once, only on the client (not on the server), immediately after the initial rendering occurs. At this point in the lifecycle, the component has a DOM representation which you can access via React.findDOMNode(this)

componentDidMount

Lifecycle Methods contd...

componentWillReceiveProps

TODO

TODO

shouldComponentUpdate

Lifecycle Methods contd...

componentWillUpdate

TODO

TODO

componentDidUpdate

Lifecycle Methods contd...

componentWillUnmount

TODO

ReactDemo

What is Flux

Also brought to you by Facebook

Uni-directional data flow

Works great with React

More of a pattern, than a framework

Pub/Sub pattern

How does it work

With more info

A Closer look…

Major parts of a Flux app

o Dispatcher

o Stores

o Views (React components)

Dispatcher

o Singleton that is the central hub for an app

o When new data comes it propagates to all stores through callbacks

o Propagation triggered by dispatch()

Dispatcher

var Dispatcher = require('flux').Dispatcher;var assign = require('object-assign');var PayloadSources = require('../constants/PayloadSources');

function throwExceptionIfActionNotSpecified(action) { if (!action.type) { throw new Error('Action type was not provided'); }}

var AppDispatcher = assign(new Dispatcher(), { handleServerAction: function(action) { console.info('server action', action); throwExceptionIfActionNotSpecified(action); this.dispatch({ source: PayloadSources.SERVER_ACTION, action: action }); },

handleViewAction: function(action) { console.info('view action', action); throwExceptionIfActionNotSpecified(action); this.dispatch({ source: PayloadSources.VIEW_ACTION, action: action }); }});

module.exports = AppDispatcher;

ActionCreators

o a library of helper methods 

o create the action object and pass the action to the dispatcher

o flow into the stores through the callbacks they define and register

ActionCreators

var AppDispatcher = require('../dispatcher/AppDispatcher');var CharacterApiUtils = require('../utils/CharacterApiUtils');var CharacterConstants = require('../constants/CharacterConstants');

var CharacterActions = {

receiveAll: function(characters) { AppDispatcher.handleServerAction({ type: CharacterConstants.ActionTypes.RECEIVE_CHARACTERS, characters: characters }); },

loadAll: function() { CharacterApiUtils.getCharacters(CharacterActions.receiveAll); }

};

module.exports = CharacterActions;

CharacterConstants

var ApiConstants = require('./ApiConstants');var keymirror = require('keymirror');

module.exports = { ApiEndPoints: { CHARACTER_GET: ApiConstants.API_ROOT + '/Character' }, ActionTypes: keymirror({ RECEIVE_CHARACTERS: null })};

CharacterApiUtils

var $ = require('jquery');var CharacterConstants = require('../constants/CharacterConstants');

var CharacterApiUtils = { getCharacters: function(successCallback) { $.get(CharacterConstants.ApiEndPoints.CHARACTER_GET) .done(function(data) { successCallback(data); }); }};

module.exports = CharacterApiUtils;

Stores

o Contain application state and logic

o Singleton

o Similar to MVC, except they manage state of more than one object

o Registers itself with the dispatcher through callbacks

o When updated, they broadcast a change event for views that are listening

Stores var AppDispatcher = require('../dispatcher/AppDispatcher');var EventEmitter = require('events').EventEmitter;var CharacterConstants = require('../constants/CharacterConstants');var assign = require('object-assign');var CHANGE_EVENT = 'change';var _characters = [];var CharacterStore = assign({}, EventEmitter.prototype, { init: function(characters) { characters.forEach(function(character) { _characters[character.id] = character; }, this); }, getAll: function() { return _characters; }, emitChange: function() { this.emit(CHANGE_EVENT); }, addChangeListener: function(callback) { this.on(CHANGE_EVENT, callback); }, removeChangeListener: function(callback) { this.removeChangeListener(CHANGE_EVENT, callback); }});AppDispatcher.register(function(payload) { var action = payload.action; switch (action.type) { case CharacterConstants.ActionTypes.RECEIVE_CHARACTERS: CharacterStore.init(action.characters); CharacterStore.emitChange(); break; }});module.exports = CharacterStore;

FluxDemo

Implementation First Phase

1. Learn how to do this stuff on your own time

2. Start simple (JSHINT, JSCS)

3. Use Change management principles Up to you to explain what is in it for them

Change Management

Implement React and Flux

If using ASP.NET MVC try React.Net firstUse on the next feature you work on (may require you

spending your own private time)Write a blog/wiki on the experience

Then let others have their input/concerns heard

Chrome Dev Tools

PostmanJSON prettyReact plugin

Thank you!

@JeffDutraCanada

[email protected]

https://github.com/jefferydutra/